Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/scripts/create-github-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ gh release create "$VERSION" \
.genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \
.genreleases/spec-kit-template-trae-sh-"$VERSION".zip \
.genreleases/spec-kit-template-trae-ps-"$VERSION".zip \
--title "Spec Kit Templates - $VERSION_NO_V" \
--notes-file release_notes.md
5 changes: 4 additions & 1 deletion .github/workflows/scripts/create-release-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,16 @@ build_variant() {
q)
mkdir -p "$base_dir/.amazonq/prompts"
generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;;
trae)
mkdir -p "$base_dir/.trae/workflows"
generate_commands trae md "\$ARGUMENTS" "$base_dir/.trae/workflows" "$script" ;;
esac
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
}

# Determine agent list
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp q)
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp q trae)
ALL_SCRIPTS=(sh ps)

norm_list() {
Expand Down
37 changes: 35 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ Specify supports multiple AI agents by generating agent-specific command files a
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
| **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI |
| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI |
| **Trae AI** | `.trae/workflows/` | Markdown | `trae` | Trae AI IDE-based Agent |
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |


### Step-by-Step Integration Guide

Follow these steps to add a new agent (using a hypothetical new agent as an example):
Expand All @@ -57,6 +59,16 @@ Follow these steps to add a new agent (using a hypothetical new agent as an exam
Add the new agent to the `AGENT_CONFIG` dictionary in `src/specify_cli/__init__.py`. This is the **single source of truth** for all agent metadata:

```python
AI_CHOICES = {
"copilot": "GitHub Copilot",
"claude": "Claude Code",
"gemini": "Gemini CLI",
"cursor": "Cursor",
"qwen": "Qwen Code",
"opencode": "opencode",
"windsurf": "Windsurf",
"q": "Amazon Q Developer CLI",
"trae": "Trae AI" # Add new agent here
AGENT_CONFIG = {
# ... existing agents ...
"new-agent-cli": { # Use the ACTUAL CLI tool name (what users type in terminal)
Expand All @@ -73,6 +85,22 @@ AGENT_CONFIG = {
- ✅ Use `"cursor-agent"` because the CLI tool is literally called `cursor-agent`
- ❌ Don't use `"cursor"` as a shortcut if the tool is `cursor-agent`

```python
agent_folder_map = {
"claude": ".claude/",
"gemini": ".gemini/",
"cursor": ".cursor/",
"qwen": ".qwen/",
"opencode": ".opencode/",
"codex": ".codex/",
"windsurf": ".windsurf/",
"kilocode": ".kilocode/",
"auggie": ".auggie/",
"copilot": ".github/",
"q": ".amazonq/",
"trae": ".trae/" # Add new agent folder here
}
```
This eliminates the need for special-case mappings throughout the codebase.

**Field Explanations**:
Expand Down Expand Up @@ -108,7 +136,7 @@ Modify `.github/workflows/scripts/create-release-packages.sh`:
##### Add to ALL_AGENTS array

```bash
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf q)
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf q trae)
```

##### Add case statement for directory structure
Expand All @@ -118,7 +146,10 @@ case $agent in
# ... existing cases ...
windsurf)
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" ;;
trae)
mkdir -p "$base_dir/.trae/workflows"
generate_commands trae md "$ARGUMENTS" "$base_dir/.trae/workflows" "$script" ;;
esac
```

Expand All @@ -131,6 +162,8 @@ gh release create "$VERSION" \
# ... existing packages ...
.genreleases/spec-kit-template-windsurf-sh-"$VERSION".zip \
.genreleases/spec-kit-template-windsurf-ps-"$VERSION".zip \
.genreleases/spec-kit-template-trae-sh-"$VERSION".zip \
.genreleases/spec-kit-template-trae-ps-"$VERSION".zip \
# Add new agent packages here
```

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
| [Roo Code](https://roocode.com/) | ✅ | |
| [Codex CLI](https://github.com/openai/codex) | ✅ | |
| [Amazon Q Developer CLI](https://aws.amazon.com/developer/learning/q-developer-cli/) | ⚠️ | Amazon Q Developer CLI [does not support](https://github.com/aws/amazon-q-developer-cli/issues/3064) custom arguments for slash commands. |
| [Trae](https://trae.ai/) | ✅ | |
| [Amp](https://ampcode.com/) | ✅ | |

## 🔧 Specify CLI Reference
Expand All @@ -160,14 +161,14 @@ The `specify` command supports the following options:
| Command | Description |
|-------------|----------------------------------------------------------------|
| `init` | Initialize a new Specify project from the latest template |
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`) |
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`, `trae`) |

### `specify init` Arguments & Options

| Argument/Option | Type | Description |
|------------------------|----------|------------------------------------------------------------------------------|
| `<project-name>` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) |
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, or `q` |
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `trae`, `codebuddy`, `amp`, or `q` |
| `--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 |
Expand Down
3 changes: 2 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Prerequisites

- **Linux/macOS** (or Windows; PowerShell scripts now supported without WSL)
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Codebuddy CLI](https://www.codebuddy.ai/cli) or [Gemini CLI](https://github.com/google-gemini/gemini-cli)
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Codebuddy CLI](https://www.codebuddy.ai/cli) or [Gemini CLI](https://github.com/google-gemini/gemini-cli), or [Trae](https://trae.ai/)
- [uv](https://docs.astral.sh/uv/) for package management
- [Python 3.11+](https://www.python.org/downloads/)
- [Git](https://git-scm.com/downloads)
Expand Down Expand Up @@ -34,6 +34,7 @@ You can proactively specify your AI agent during initialization:
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai claude
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai gemini
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai copilot
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai trae
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai codebuddy
```

Expand Down
13 changes: 11 additions & 2 deletions scripts/bash/update-agent-context.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
AMP_FILE="$REPO_ROOT/AGENTS.md"
Q_FILE="$REPO_ROOT/AGENTS.md"
TRAE_FILE="$REPO_ROOT/.trae/rules/specify-rules.md"

# Template file
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
Expand Down Expand Up @@ -621,9 +622,12 @@ update_specific_agent() {
q)
update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
;;
trae)
update_agent_file "$TRAE_FILE" "Trae IDE"
;;
*)
log_error "Unknown agent type '$agent_type'"
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|q"
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|q|trae"
exit 1
;;
esac
Expand Down Expand Up @@ -693,6 +697,11 @@ update_all_existing_agents() {
found_agent=true
fi

if [[ -f "$TRAE_FILE" ]]; then
update_agent_file "$TRAE_FILE" "Trae IDE"
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..."
Expand All @@ -717,7 +726,7 @@ print_summary() {

echo

log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|q]"
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|q|trae]"
}

#==============================================================================
Expand Down
8 changes: 5 additions & 3 deletions scripts/powershell/update-agent-context.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1
#>
param(
[Parameter(Position=0)]
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','q')]
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','q','trae')]
[string]$AgentType
)

Expand Down Expand Up @@ -57,6 +57,7 @@ $ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$TRAE_FILE = Join-Path $REPO_ROOT '.trae/rules/specify-rules.md'

$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'

Expand Down Expand Up @@ -382,7 +383,7 @@ function Update-SpecificAgent {
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q'; return $false }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|trae|codebuddy|amp|q'; return $false }
}
}

Expand All @@ -401,6 +402,7 @@ function Update-AllExistingAgents {
if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true }
if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true }
if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true }
if (Test-Path $TRAE_FILE) { if (-not (Update-AgentFile -TargetFile $TRAE_FILE -AgentName 'Trae IDE')) { $ok = $false }; $found = $true }
if (-not $found) {
Write-Info 'No existing agent files found, creating default Claude file...'
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
Expand All @@ -415,7 +417,7 @@ function Print-Summary {
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
Write-Host ''
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q]'
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|trae|codebuddy|amp|q]'
}

function Main {
Expand Down
74 changes: 71 additions & 3 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ def _github_auth_headers(cli_token: str | None = None) -> dict:
"install_url": "https://ampcode.com/manual#install",
"requires_cli": True,
},
"trae-agent": {
"name": "Trae",
"folder": ".trae/",
"install_url": None, # IDE-based, no CLI check needed
"requires_cli": False,
},
}

SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
Expand Down Expand Up @@ -818,6 +824,59 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
return project_path


def extract_local_template(zip_path: Path, project_path: Path, is_current_dir: bool = False, *, verbose: bool = True, tracker: StepTracker | None = None) -> Path:
"""Extract a local template zip into project_path.

This mirrors the minimal behavior of download_and_extract_template but
uses a locally-provided zip file instead of downloading from GitHub.
"""
if tracker:
tracker.start("extract", "Extract local template")
elif verbose:
console.print("Extracting local template...")

try:
if not is_current_dir:
project_path.mkdir(parents=True, exist_ok=True)

with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_contents = zip_ref.namelist()
if tracker:
tracker.start("zip-list")
tracker.complete("zip-list", f"{len(zip_contents)} entries")
elif verbose:
console.print(f"[cyan]ZIP contains {len(zip_contents)} items[/cyan]")

if is_current_dir:
# Extract into a temp dir first then merge to cwd to avoid
# accidental overwrites of working dir metadata.
with tempfile.TemporaryDirectory() as temp_dir:
zip_ref.extractall(temp_dir)
for item in Path(temp_dir).iterdir():
dest = Path.cwd() / item.name
if item.is_dir():
shutil.copytree(item, dest, dirs_exist_ok=True)
else:
shutil.copy2(item, dest)
else:
zip_ref.extractall(project_path)

except Exception as e:
if tracker:
tracker.error("extract", str(e))
else:
if verbose:
console.print(f"[red]Error extracting local template:[/red] {e}")
# Clean up partially created folder on error
if not is_current_dir and project_path.exists():
shutil.rmtree(project_path)
raise
else:
if tracker:
tracker.complete("extract")
return project_path


def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None = None) -> None:
"""Ensure POSIX .sh scripts under .specify/scripts (recursively) have execute bits (no-op on Windows)."""
if os.name == "nt":
Expand Down Expand Up @@ -865,8 +924,9 @@ 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, or use '.' for current directory)"),
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, or q"),
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, trae, codebuddy, amp, or q"),
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"),
template_file: str = typer.Option(None, "--template-file", help="Path to local template zip to use instead of downloading from GitHub"),
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"),
Expand Down Expand Up @@ -1044,7 +1104,16 @@ def init(
local_ssl_context = ssl_context if verify else False
local_client = httpx.Client(verify=local_ssl_context)

download_and_extract_template(project_path, selected_ai, selected_script, here, verbose=False, tracker=tracker, client=local_client, debug=debug, github_token=github_token)
if template_file:
zip_path = Path(template_file).expanduser()
if not zip_path.exists():
tracker.error("fetch", f"Local template not found: {zip_path}")
console.print(Panel(f"Local template file not found: [cyan]{zip_path}[/cyan]", title="Template Error", border_style="red"))
raise typer.Exit(1)
# Use local template zip
extract_local_template(zip_path, project_path, here, verbose=False, tracker=tracker)
else:
download_and_extract_template(project_path, selected_ai, selected_script, here, verbose=False, tracker=tracker, client=local_client, debug=debug, github_token=github_token)

ensure_executable_scripts(project_path, tracker=tracker)

Expand Down Expand Up @@ -1103,7 +1172,6 @@ def init(
)
console.print(git_error_panel)

# Agent folder security notice
agent_config = AGENT_CONFIG.get(selected_ai)
if agent_config:
agent_folder = agent_config["folder"]
Expand Down