Merge pull request #14 from honjo-hiroaki-gtt/feat/add-codex-support

feat: Add Codex CLI support
This commit is contained in:
Den Delimarsky
2025-09-20 10:28:40 -07:00
committed by GitHub
9 changed files with 82 additions and 35 deletions

View File

@@ -81,7 +81,7 @@ jobs:
cat > release_notes.md << EOF cat > release_notes.md << EOF
Template release ${{ steps.get_tag.outputs.new_version }} Template release ${{ steps.get_tag.outputs.new_version }}
Updated specification-driven development templates for GitHub Copilot, Claude Code, Gemini CLI, Cursor, Qwen, opencode, and Windsurf. Updated specification-driven development templates for GitHub Copilot, Claude Code, Gemini CLI, Cursor, Qwen, opencode, Windsurf, and Codex.
Now includes per-script variants for POSIX shell (sh) and PowerShell (ps). Now includes per-script variants for POSIX shell (sh) and PowerShell (ps).
@@ -100,6 +100,8 @@ jobs:
- spec-kit-template-qwen-ps-${{ steps.get_tag.outputs.new_version }}.zip - spec-kit-template-qwen-ps-${{ steps.get_tag.outputs.new_version }}.zip
- spec-kit-template-windsurf-sh-${{ steps.get_tag.outputs.new_version }}.zip - spec-kit-template-windsurf-sh-${{ steps.get_tag.outputs.new_version }}.zip
- spec-kit-template-windsurf-ps-${{ steps.get_tag.outputs.new_version }}.zip - 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
EOF EOF
echo "Generated release notes:" echo "Generated release notes:"
@@ -126,6 +128,8 @@ jobs:
spec-kit-template-qwen-ps-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-qwen-ps-${{ steps.get_tag.outputs.new_version }}.zip \
spec-kit-template-windsurf-sh-${{ steps.get_tag.outputs.new_version }}.zip \ spec-kit-template-windsurf-sh-${{ steps.get_tag.outputs.new_version }}.zip \
spec-kit-template-windsurf-ps-${{ steps.get_tag.outputs.new_version }}.zip \ 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 \
--title "Spec Kit Templates - $VERSION_NO_V" \ --title "Spec Kit Templates - $VERSION_NO_V" \
--notes-file release_notes.md --notes-file release_notes.md
env: env:

View File

@@ -6,7 +6,7 @@ set -euo pipefail
# Usage: .github/workflows/scripts/create-release-packages.sh <version> # Usage: .github/workflows/scripts/create-release-packages.sh <version>
# Version argument should include leading 'v'. # Version argument should include leading 'v'.
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built. # Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
# AGENTS : space or comma separated subset of: claude gemini copilot qwen opencode (default: all) # AGENTS : space or comma separated subset of: claude gemini copilot cursor qwen opencode windsurf codex (default: all)
# SCRIPTS : space or comma separated subset of: sh ps (default: both) # SCRIPTS : space or comma separated subset of: sh ps (default: both)
# Examples: # Examples:
# AGENTS=claude SCRIPTS=sh $0 v0.2.0 # AGENTS=claude SCRIPTS=sh $0 v0.2.0
@@ -157,13 +157,16 @@ build_variant() {
windsurf) windsurf)
mkdir -p "$base_dir/.windsurf/workflows" mkdir -p "$base_dir/.windsurf/workflows"
generate_commands windsurf md "\$ARGUMENTS" "$base_dir/.windsurf/workflows" "$script" ;; generate_commands windsurf md "\$ARGUMENTS" "$base_dir/.windsurf/workflows" "$script" ;;
codex)
mkdir -p "$base_dir/.codex/commands"
generate_commands codex md "\$ARGUMENTS" "$base_dir/.codex/commands" "$script" ;;
esac esac
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . ) ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
} }
# Determine agent list # Determine agent list
ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf) ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf codex)
ALL_SCRIPTS=(sh ps) ALL_SCRIPTS=(sh ps)

View File

@@ -5,6 +5,13 @@ All notable changes to the Specify CLI will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.0.11] - 2025-09-20
### Added
- Codex CLI support (thank you [@honjo-hiroaki-gtt](https://github.com/honjo-hiroaki-gtt) for the contribution in [#14](https://github.com/github/spec-kit/pull/14))
- Codex-aware context update tooling (Bash and PowerShell) so feature plans refresh `AGENTS.md` alongside existing assistants without manual edits.
## [0.0.10] - 2025-09-20 ## [0.0.10] - 2025-09-20
### Fixed ### Fixed
@@ -21,7 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Windsurf IDE support as additional AI assistant option - Windsurf IDE support as additional AI assistant option (thank you [@raedkit](https://github.com/raedkit) for the work in [#151](https://github.com/github/spec-kit/pull/151))
- GitHub token support for API requests to handle corporate environments and rate limiting (contributed by [@zryfish](https://github.com/@zryfish) in [#243](https://github.com/github/spec-kit/pull/243)) - GitHub token support for API requests to handle corporate environments and rate limiting (contributed by [@zryfish](https://github.com/@zryfish) in [#243](https://github.com/github/spec-kit/pull/243))
### Changed ### Changed
@@ -62,4 +69,3 @@ N/A
### Changed ### Changed
N/A N/A

View File

@@ -209,7 +209,7 @@ Our research and experimentation focus on:
## 🔧 Prerequisites ## 🔧 Prerequisites
- **Linux/macOS** (or WSL2 on Windows) - **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/), 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), or [Windsurf](https://windsurf.com/)
- [uv](https://docs.astral.sh/uv/) for package management - [uv](https://docs.astral.sh/uv/) for package management
- [Python 3.11+](https://www.python.org/downloads/) - [Python 3.11+](https://www.python.org/downloads/)
- [Git](https://git-scm.com/downloads) - [Git](https://git-scm.com/downloads)
@@ -246,14 +246,17 @@ You will be prompted to select the AI agent you are using. You can also proactiv
specify init <project_name> --ai claude specify init <project_name> --ai claude
specify init <project_name> --ai gemini specify init <project_name> --ai gemini
specify init <project_name> --ai copilot specify init <project_name> --ai copilot
specify init <project_name> --ai cursor
specify init <project_name> --ai qwen specify init <project_name> --ai qwen
specify init <project_name> --ai opencode specify init <project_name> --ai opencode
specify init <project_name> --ai codex
specify init <project_name> --ai windsurf specify init <project_name> --ai windsurf
# Or in current directory: # Or in current directory:
specify init --here --ai claude specify init --here --ai claude
specify init --here --ai codex
``` ```
The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, or opencode installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command: The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, or Codex CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
```bash ```bash
specify init <project_name> --ai claude --ignore-agent-tools specify init <project_name> --ai claude --ignore-agent-tools

View File

@@ -18,7 +18,21 @@ if [ -z "$FEATURE_DESCRIPTION" ]; then
exit 1 exit 1
fi fi
REPO_ROOT=$(git rev-parse --show-toplevel) # Resolve repository root. Prefer git information when available, but fall back
# to the script location so the workflow still functions in repositories that
# were initialised with --no-git.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FALLBACK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if git rev-parse --show-toplevel >/dev/null 2>&1; then
REPO_ROOT=$(git rev-parse --show-toplevel)
HAS_GIT=true
else
REPO_ROOT="$FALLBACK_ROOT"
HAS_GIT=false
fi
cd "$REPO_ROOT"
SPECS_DIR="$REPO_ROOT/specs" SPECS_DIR="$REPO_ROOT/specs"
mkdir -p "$SPECS_DIR" mkdir -p "$SPECS_DIR"
@@ -40,7 +54,11 @@ BRANCH_NAME=$(echo "$FEATURE_DESCRIPTION" | tr '[:upper:]' '[:lower:]' | sed 's/
WORDS=$(echo "$BRANCH_NAME" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//') WORDS=$(echo "$BRANCH_NAME" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//')
BRANCH_NAME="${FEATURE_NUM}-${WORDS}" BRANCH_NAME="${FEATURE_NUM}-${WORDS}"
git checkout -b "$BRANCH_NAME" if [ "$HAS_GIT" = true ]; then
git checkout -b "$BRANCH_NAME"
else
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
fi
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME" FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
mkdir -p "$FEATURE_DIR" mkdir -p "$FEATURE_DIR"

View File

@@ -54,15 +54,16 @@ case "$AGENT_TYPE" in
cursor) update_agent_file "$CURSOR_FILE" "Cursor IDE" ;; cursor) update_agent_file "$CURSOR_FILE" "Cursor IDE" ;;
qwen) update_agent_file "$QWEN_FILE" "Qwen Code" ;; qwen) update_agent_file "$QWEN_FILE" "Qwen Code" ;;
opencode) update_agent_file "$AGENTS_FILE" "opencode" ;; opencode) update_agent_file "$AGENTS_FILE" "opencode" ;;
codex) update_agent_file "$AGENTS_FILE" "Codex CLI" ;;
windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;; windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;;
"") [ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"; \ "") [ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"; \
[ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI"; \ [ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI"; \
[ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"; \ [ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"; \
[ -f "$CURSOR_FILE" ] && update_agent_file "$CURSOR_FILE" "Cursor IDE"; \ [ -f "$CURSOR_FILE" ] && update_agent_file "$CURSOR_FILE" "Cursor IDE"; \
[ -f "$QWEN_FILE" ] && update_agent_file "$QWEN_FILE" "Qwen Code"; \ [ -f "$QWEN_FILE" ] && update_agent_file "$QWEN_FILE" "Qwen Code"; \
[ -f "$AGENTS_FILE" ] && update_agent_file "$AGENTS_FILE" "opencode"; \ [ -f "$AGENTS_FILE" ] && update_agent_file "$AGENTS_FILE" "Codex/opencode"; \
[ -f "$WINDSURF_FILE" ] && update_agent_file "$WINDSURF_FILE" "Windsurf"; \ [ -f "$WINDSURF_FILE" ] && update_agent_file "$WINDSURF_FILE" "Windsurf"; \
if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ] && [ ! -f "$CURSOR_FILE" ] && [ ! -f "$QWEN_FILE" ] && [ ! -f "$AGENTS_FILE" ] && [ ! -f "$WINDSURF_FILE" ]; then update_agent_file "$CLAUDE_FILE" "Claude Code"; fi ;; if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ] && [ ! -f "$CURSOR_FILE" ] && [ ! -f "$QWEN_FILE" ] && [ ! -f "$AGENTS_FILE" ] && [ ! -f "$WINDSURF_FILE" ]; then update_agent_file "$CLAUDE_FILE" "Claude Code"; fi ;;
*) echo "ERROR: Unknown agent type '$AGENT_TYPE' (expected claude|gemini|copilot|cursor|qwen|opencode|windsurf)"; exit 1 ;; *) echo "ERROR: Unknown agent type '$AGENT_TYPE' (expected claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf)"; exit 1 ;;
esac esac
echo; echo "Summary of changes:"; [ -n "$NEW_LANG" ] && echo "- Added language: $NEW_LANG"; [ -n "$NEW_FRAMEWORK" ] && echo "- Added framework: $NEW_FRAMEWORK"; [ -n "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ] && echo "- Added database: $NEW_DB"; echo; echo "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|windsurf]" echo; echo "Summary of changes:"; [ -n "$NEW_LANG" ] && echo "- Added language: $NEW_LANG"; [ -n "$NEW_FRAMEWORK" ] && echo "- Added framework: $NEW_FRAMEWORK"; [ -n "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ] && echo "- Added database: $NEW_DB"; echo; echo "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf]"

View File

@@ -77,6 +77,7 @@ switch ($AgentType) {
'qwen' { Update-AgentFile $qwenFile 'Qwen Code' } 'qwen' { Update-AgentFile $qwenFile 'Qwen Code' }
'opencode' { Update-AgentFile $agentsFile 'opencode' } 'opencode' { Update-AgentFile $agentsFile 'opencode' }
'windsurf' { Update-AgentFile $windsurfFile 'Windsurf' } 'windsurf' { Update-AgentFile $windsurfFile 'Windsurf' }
'codex' { Update-AgentFile $agentsFile 'Codex CLI' }
'' { '' {
foreach ($pair in @( foreach ($pair in @(
@{file=$claudeFile; name='Claude Code'}, @{file=$claudeFile; name='Claude Code'},
@@ -85,7 +86,8 @@ switch ($AgentType) {
@{file=$cursorFile; name='Cursor IDE'}, @{file=$cursorFile; name='Cursor IDE'},
@{file=$qwenFile; name='Qwen Code'}, @{file=$qwenFile; name='Qwen Code'},
@{file=$agentsFile; name='opencode'}, @{file=$agentsFile; name='opencode'},
@{file=$windsurfFile; name='Windsurf'} @{file=$windsurfFile; name='Windsurf'},
@{file=$agentsFile; name='Codex CLI'}
)) { )) {
if (Test-Path $pair.file) { Update-AgentFile $pair.file $pair.name } if (Test-Path $pair.file) { Update-AgentFile $pair.file $pair.name }
} }
@@ -94,7 +96,7 @@ switch ($AgentType) {
Update-AgentFile $claudeFile 'Claude Code' Update-AgentFile $claudeFile 'Claude Code'
} }
} }
Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, cursor, qwen, opencode, windsurf or leave empty for all."; exit 1 } Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, cursor, qwen, opencode, windsurf, codex or leave empty for all."; exit 1 }
} }
Write-Output '' Write-Output ''
@@ -104,4 +106,4 @@ if ($newFramework) { Write-Output "- Added framework: $newFramework" }
if ($newDb -and $newDb -ne 'N/A') { Write-Output "- Added database: $newDb" } if ($newDb -and $newDb -ne 'N/A') { Write-Output "- Added database: $newDb" }
Write-Output '' Write-Output ''
Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot|cursor|qwen|opencode|windsurf]' Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot|cursor|qwen|opencode|windsurf|codex]'

View File

@@ -69,7 +69,8 @@ AI_CHOICES = {
"cursor": "Cursor", "cursor": "Cursor",
"qwen": "Qwen Code", "qwen": "Qwen Code",
"opencode": "opencode", "opencode": "opencode",
"windsurf": "Windsurf" "codex": "Codex CLI",
"windsurf": "Windsurf",
} }
# Add script type choices # Add script type choices
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
@@ -461,20 +462,21 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
raise typer.Exit(1) raise typer.Exit(1)
# Find the template asset for the specified AI assistant # Find the template asset for the specified AI assistant
assets = release_data.get("assets", [])
pattern = f"spec-kit-template-{ai_assistant}-{script_type}" pattern = f"spec-kit-template-{ai_assistant}-{script_type}"
matching_assets = [ matching_assets = [
asset for asset in release_data.get("assets", []) asset for asset in assets
if pattern in asset["name"] and asset["name"].endswith(".zip") if pattern in asset["name"] and asset["name"].endswith(".zip")
] ]
if not matching_assets: asset = matching_assets[0] if matching_assets else None
console.print(f"[red]No matching release asset found[/red] for pattern: [bold]{pattern}[/bold]")
asset_names = [a.get('name','?') for a in release_data.get('assets', [])] if asset is None:
console.print(f"[red]No matching release asset found[/red] for [bold]{ai_assistant}[/bold] (expected pattern: [bold]{pattern}[/bold])")
asset_names = [a.get('name', '?') for a in assets]
console.print(Panel("\n".join(asset_names) or "(no assets)", title="Available Assets", border_style="yellow")) console.print(Panel("\n".join(asset_names) or "(no assets)", title="Available Assets", border_style="yellow"))
raise typer.Exit(1) raise typer.Exit(1)
# Use the first matching asset
asset = matching_assets[0]
download_url = asset["browser_download_url"] download_url = asset["browser_download_url"]
filename = asset["name"] filename = asset["name"]
file_size = asset["size"] file_size = asset["size"]
@@ -483,14 +485,12 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
console.print(f"[cyan]Found template:[/cyan] {filename}") console.print(f"[cyan]Found template:[/cyan] {filename}")
console.print(f"[cyan]Size:[/cyan] {file_size:,} bytes") console.print(f"[cyan]Size:[/cyan] {file_size:,} bytes")
console.print(f"[cyan]Release:[/cyan] {release_data['tag_name']}") console.print(f"[cyan]Release:[/cyan] {release_data['tag_name']}")
# Download the file
zip_path = download_dir / filename zip_path = download_dir / filename
if verbose: if verbose:
console.print(f"[cyan]Downloading template...[/cyan]") console.print(f"[cyan]Downloading template...[/cyan]")
try: try:
# Include auth header for initial GitHub request; it won't leak across cross-origin redirects
with client.stream( with client.stream(
"GET", "GET",
download_url, download_url,
@@ -743,11 +743,10 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None =
for f in failures: for f in failures:
console.print(f" - {f}") console.print(f" - {f}")
@app.command() @app.command()
def init( def init(
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"), 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 or windsurf"), ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor, qwen, opencode, codex, or windsurf"),
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"), 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"), 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"), no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
@@ -761,7 +760,7 @@ def init(
This command will: This command will:
1. Check that required tools are installed (git is optional) 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 or Windsurf) 2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, or Windsurf)
3. Download the appropriate template from GitHub 3. Download the appropriate template from GitHub
4. Extract the template to a new project directory or current directory 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) 5. Initialize a fresh git repository (if not --no-git and no existing repo)
@@ -774,9 +773,12 @@ def init(
specify init my-project --ai copilot --no-git specify init my-project --ai copilot --no-git
specify init my-project --ai cursor specify init my-project --ai cursor
specify init my-project --ai qwen specify init my-project --ai qwen
specify init my-project --ai opencode
specify init my-project --ai codex
specify init my-project --ai windsurf specify init my-project --ai windsurf
specify init --ignore-agent-tools my-project specify init --ignore-agent-tools my-project
specify init --here --ai claude specify init --here --ai claude
specify init --here --ai codex
specify init --here specify init --here
""" """
# Show banner first # Show banner first
@@ -871,6 +873,10 @@ def init(
if not check_tool("opencode", "Install from: https://opencode.ai"): if not check_tool("opencode", "Install from: https://opencode.ai"):
console.print("[red]Error:[/red] opencode CLI is required for opencode projects") console.print("[red]Error:[/red] opencode CLI is required for opencode projects")
agent_tool_missing = True agent_tool_missing = True
elif selected_ai == "codex":
if not check_tool("codex", "Install from: https://github.com/openai/codex"):
console.print("[red]Error:[/red] Codex CLI is required for Codex projects")
agent_tool_missing = True
# GitHub Copilot and Cursor checks are not needed as they're typically available in supported IDEs # GitHub Copilot and Cursor checks are not needed as they're typically available in supported IDEs
if agent_tool_missing: if agent_tool_missing:
@@ -914,7 +920,7 @@ def init(
("extract", "Extract template"), ("extract", "Extract template"),
("zip-list", "Archive contents"), ("zip-list", "Archive contents"),
("extracted-summary", "Extraction summary"), ("extracted-summary", "Extraction summary"),
("chmod", "Ensure scripts executable"), ("chmod", "Ensure scripts executable"),
("cleanup", "Cleanup"), ("cleanup", "Cleanup"),
("git", "Initialize git repository"), ("git", "Initialize git repository"),
("final", "Finalize") ("final", "Finalize")
@@ -1010,6 +1016,7 @@ def check():
tracker.add("cursor-agent", "Cursor IDE agent (optional)") tracker.add("cursor-agent", "Cursor IDE agent (optional)")
tracker.add("windsurf", "Windsurf IDE (optional)") tracker.add("windsurf", "Windsurf IDE (optional)")
tracker.add("opencode", "opencode") tracker.add("opencode", "opencode")
tracker.add("codex", "Codex CLI")
git_ok = check_tool_for_tracker("git", "https://git-scm.com/downloads", tracker) git_ok = check_tool_for_tracker("git", "https://git-scm.com/downloads", tracker)
claude_ok = check_tool_for_tracker("claude", "https://docs.anthropic.com/en/docs/claude-code/setup", tracker) claude_ok = check_tool_for_tracker("claude", "https://docs.anthropic.com/en/docs/claude-code/setup", tracker)
@@ -1021,14 +1028,15 @@ def check():
cursor_ok = check_tool_for_tracker("cursor-agent", "https://cursor.sh/", tracker) cursor_ok = check_tool_for_tracker("cursor-agent", "https://cursor.sh/", tracker)
windsurf_ok = check_tool_for_tracker("windsurf", "https://windsurf.com/", tracker) windsurf_ok = check_tool_for_tracker("windsurf", "https://windsurf.com/", tracker)
opencode_ok = check_tool_for_tracker("opencode", "https://opencode.ai/", tracker) opencode_ok = check_tool_for_tracker("opencode", "https://opencode.ai/", tracker)
codex_ok = check_tool_for_tracker("codex", "https://github.com/openai/codex", tracker)
console.print(tracker.render()) console.print(tracker.render())
console.print("\n[bold green]Specify CLI is ready to use![/bold green]") console.print("\n[bold green]Specify CLI is ready to use![/bold green]")
if not git_ok: if not git_ok:
console.print("[dim]Tip: Install git for repository management[/dim]") console.print("[dim]Tip: Install git for repository management[/dim]")
if not (claude_ok or gemini_ok or cursor_ok or qwen_ok or windsurf_ok or opencode_ok): if not (claude_ok or gemini_ok or cursor_ok or qwen_ok or windsurf_ok or opencode_ok or codex_ok):
console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]") console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]")

View File

@@ -5,7 +5,9 @@ scripts:
ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}" ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"
--- ---
Given the feature description provided as an argument, do this: The text the user typed after `/specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{ARGS}` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
Given that feature description, do this:
1. Run the script `{SCRIPT}` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute. 1. Run the script `{SCRIPT}` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
**IMPORTANT** You must only ever run this script once. The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for. **IMPORTANT** You must only ever run this script once. The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for.