From 74f66b617b7569bfe0542da879e4299969408e49 Mon Sep 17 00:00:00 2001 From: Brandon Braner Date: Thu, 11 Sep 2025 18:14:13 -0500 Subject: [PATCH] Add OpenCode support to Specify CLI and release workflow - Updated AI_CHOICES to include OpenCode. - Modified init command to allow selection of OpenCode as an AI assistant. - Enhanced release workflow to create and package OpenCode templates. --- .github/workflows/release.yml | 17 +++++++++++++++-- src/specify_cli/__init__.py | 24 ++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1ebfbc7ab..f1d2b2adcc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -146,6 +146,13 @@ jobs: generate_commands "copilot" "prompt.md" "\$ARGUMENTS" "sdd-copilot-package/.github/prompts" echo "Created GitHub Copilot package" + # Create opencode package + mkdir -p sdd-opencode-package + cp -r sdd-package-base/* sdd-opencode-package/ + mkdir -p sdd-opencode-package/.opencode/commands + generate_commands "opencode" "md" "\$ARGUMENTS" "sdd-opencode-package/.opencode/commands" + echo "Created opencode package" + # Create archive files for each package cd sdd-claude-package && zip -r ../spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip . && cd .. @@ -153,6 +160,8 @@ jobs: cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip . && cd .. + cd sdd-opencode-package && zip -r ../spec-kit-template-opencode-${{ steps.get_tag.outputs.new_version }}.zip . && cd .. + # List contents for verification echo "Claude package contents:" unzip -l spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip | head -10 @@ -160,6 +169,8 @@ jobs: unzip -l spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip | head -10 echo "Copilot package contents:" unzip -l spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip | head -10 + echo "opencode package contents:" + unzip -l spec-kit-template-opencode-${{ steps.get_tag.outputs.new_version }}.zip | head -10 - name: Generate release notes if: steps.check_release.outputs.exists == 'false' @@ -183,12 +194,13 @@ jobs: cat > release_notes.md << EOF Template release ${{ steps.get_tag.outputs.new_version }} - Updated specification-driven development templates for GitHub Copilot, Claude Code, and Gemini CLI. + Updated specification-driven development templates for GitHub Copilot, Claude Code, Gemini CLI, and opencode. Download the template for your preferred AI assistant: - spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip - spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip - - spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip + - spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip + - spec-kit-template-opencode-${{ steps.get_tag.outputs.new_version }}.zip EOF echo "Generated release notes:" @@ -205,6 +217,7 @@ jobs: spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip \ + spec-kit-template-opencode-${{ steps.get_tag.outputs.new_version }}.zip \ --title "Spec Kit Templates - $VERSION_NO_V" \ --notes-file release_notes.md env: diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 704d354d9b..3e125a9a6e 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -56,7 +56,8 @@ AI_CHOICES = { "copilot": "GitHub Copilot", "claude": "Claude Code", - "gemini": "Gemini CLI" + "gemini": "Gemini CLI", + "opencode": "OpenCode" } # ASCII Art Banner @@ -638,7 +639,7 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, is_curr @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, or copilot"), + ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, or opencode"), 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"), here: bool = typer.Option(False, "--here", help="Initialize project in the current directory instead of creating a new one"), @@ -649,7 +650,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, or GitHub Copilot) + 2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, or OpenCode) 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) @@ -660,6 +661,7 @@ def init( specify init my-project --ai claude specify init my-project --ai gemini specify init my-project --ai copilot --no-git + specify init my-project --ai opencode specify init --ignore-agent-tools my-project specify init --here --ai claude specify init --here @@ -738,7 +740,11 @@ def init( if not check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli"): console.print("[red]Error:[/red] Gemini CLI is required for Gemini projects") agent_tool_missing = True - # GitHub Copilot check is not needed as it's typically available in supported IDEs + elif selected_ai == "opencode": + if not check_tool("opencode", "Install from: https://github.com/sst/opencode"): + console.print("[red]Error:[/red] opencode is required for opencode projects") + agent_tool_missing = True + # GitHub Copilot checks are not needed as they are typically available in supported IDEs if agent_tool_missing: console.print("\n[red]Required AI tool is missing![/red]") @@ -830,7 +836,11 @@ def init( steps_lines.append(" - See GEMINI.md for all available commands") elif selected_ai == "copilot": steps_lines.append(f"{step_num}. Open in Visual Studio Code and use [bold cyan]/specify[/], [bold cyan]/plan[/], [bold cyan]/tasks[/] commands with GitHub Copilot") - + elif selected_ai == "opencode": + steps_lines.append(f"{step_num}. Use / commands with OpenCode CLI") + steps_lines.append(" - Run opencode /specify to create specifications") + steps_lines.append(" - Run opencode /plan to create implementation plans") + steps_lines.append(" - Run opencode /tasks to generate tasks") step_num += 1 steps_lines.append(f"{step_num}. Update [bold magenta]CONSTITUTION.md[/bold magenta] with your project's non-negotiable principles") @@ -866,11 +876,13 @@ def check(skip_tls: bool = typer.Option(False, "--skip-tls", help="Skip SSL/TLS console.print("\n[cyan]Optional AI tools:[/cyan]") claude_ok = check_tool("claude", "Install from: https://docs.anthropic.com/en/docs/claude-code/setup") gemini_ok = check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli") + opencode_ok = check_tool("opencode", "Install from: https://github.com/sst/opencode") + # opencode check is not needed as it's typically available in supported IDEs console.print("\n[green]✓ Specify CLI is ready to use![/green]") if not git_ok: console.print("[yellow]Consider installing git for repository management[/yellow]") - if not (claude_ok or gemini_ok): + if not (claude_ok or gemini_ok or opencode_ok): console.print("[yellow]Consider installing an AI assistant for the best experience[/yellow]")