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 @@ -52,5 +52,7 @@ gh release create "$VERSION" \
.genreleases/spec-kit-template-agy-ps-"$VERSION".zip \
.genreleases/spec-kit-template-bob-sh-"$VERSION".zip \
.genreleases/spec-kit-template-bob-ps-"$VERSION".zip \
.genreleases/spec-kit-template-generic-sh-"$VERSION".zip \
.genreleases/spec-kit-template-generic-ps-"$VERSION".zip \
--title "Spec Kit Templates - $VERSION_NO_V" \
--notes-file release_notes.md
8 changes: 6 additions & 2 deletions .github/workflows/scripts/create-release-packages.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

.PARAMETER Agents
Comma or space separated subset of agents to build (default: all)
Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, q, bob, qoder
Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, q, bob, qoder, generic

.PARAMETER Scripts
Comma or space separated subset of script types to build (default: both)
Expand Down Expand Up @@ -347,6 +347,10 @@ function Build-Variant {
$cmdDir = Join-Path $baseDir ".qoder/commands"
Generate-Commands -Agent 'qoder' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'generic' {
$cmdDir = Join-Path $baseDir ".speckit/commands"
Generate-Commands -Agent 'generic' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
}

# Create zip archive
Expand All @@ -356,7 +360,7 @@ function Build-Variant {
}

# Define all agents and scripts
$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qoder')
$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qoder', 'generic')
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PowerShell release packaging script is missing the 'agy' (Antigravity) agent that was added to the bash version (create-release-packages.sh line 233). This inconsistency means the PowerShell script cannot build agy template packages. The 'agy' agent should be added to the $AllAgents array, and a corresponding case should be added to the Build-Variant switch statement (similar to lines 218-220 in the bash version).

Copilot uses AI. Check for mistakes.
$AllScripts = @('sh', 'ps')

function Normalize-List {
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/scripts/create-release-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -euo pipefail
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
# 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-agent qwen opencode windsurf codex amp shai bob (default: all)
# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp shai bob generic (default: all)
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
# Examples:
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
Expand Down Expand Up @@ -221,13 +221,16 @@ build_variant() {
bob)
mkdir -p "$base_dir/.bob/commands"
generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;
generic)
mkdir -p "$base_dir/.speckit/commands"
generate_commands generic md "\$ARGUMENTS" "$base_dir/.speckit/commands" "$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 shai q agy bob qoder)
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q agy bob qoder generic)
ALL_SCRIPTS=(sh ps)

norm_list() {
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Specify supports multiple AI agents by generating agent-specific command files a
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |
| **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI |
| **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE |
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'agy' (Antigravity) agent is missing from the Current Supported Agents table in AGENTS.md. Since it's been added to AGENT_CONFIG in init.py, it should also be documented here with its directory (.agent/), format (Markdown), CLI Tool (N/A IDE-based), and description for consistency with other agents.

Suggested change
| **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE |
| **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE |
| **Antigravity** | `.agent/` | Markdown | N/A (IDE-based) | Antigravity IDE |

Copilot uses AI. Check for mistakes.
| **Generic** | User-specified via `--ai-commands-dir` | Markdown | N/A | Bring your own agent |

### Step-by-Step Integration Guide

Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ All notable changes to the Specify CLI and templates are documented here.
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).

## [0.1.1] - 2026-02-13
## [0.1.1] - 2026-02-17

### Added

Expand All @@ -22,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Existing repos** (`--here`): pre-existing command files are preserved — no breaking changes
- `pyyaml` dependency (already present) used for YAML frontmatter parsing
- **Unit tests** for `install_ai_skills`, `_get_skills_dir`, and `--ai-skills` CLI validation (51 test cases covering all 18 supported agents)
- **Generic Agent Support**: Added `--ai generic` option for unsupported AI agents ("bring your own agent")
- Requires `--ai-commands-dir <path>` to specify where the agent reads commands from
- Generates Markdown commands with `$ARGUMENTS` format (compatible with most agents)
- Example: `specify init my-project --ai generic --ai-commands-dir .myagent/commands/`
- Enables users to start with Spec Kit immediately while their agent awaits formal support

Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG entry for version 0.1.1 only mentions the generic agent support but doesn't document the addition of 'agy' (Antigravity) as a new supported agent. Since Antigravity support was added in this PR (as evidenced by changes to AGENT_CONFIG, release scripts, and agent context scripts), it should be included in the CHANGELOG for completeness.

Suggested change
- **Antigravity Agent (`agy`) Support**: Added first-class support for the Antigravity agent
- Use via `--ai agy` to generate commands tailored for the Antigravity agent
- Integrated into agent configuration, release scripts, and agent context handling
- Recognized as a fully supported AI agent alongside existing agents

Copilot uses AI. Check for mistakes.
## [0.1.0] - 2026-01-28

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
| [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | |
| [Windsurf](https://windsurf.com/) | ✅ | |
| [Antigravity (agy)](https://agy.ai/) | ✅ | |
| Generic | ✅ | Bring your own agent — use `--ai generic --ai-commands-dir <path>` for unsupported agents |

## 🔧 Specify CLI Reference

Expand All @@ -179,7 +180,8 @@ The `specify` command supports the following 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`, `shai`, `q`, `agy`, `bob`, or `qoder` |
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `q`, `agy`, `bob`, `qoder`, or `generic` (requires `--ai-commands-dir`) |
| `--ai-commands-dir` | Option | Directory for agent command files (required with `--ai generic`, e.g. `.myagent/commands/`) |
| `--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 Expand Up @@ -217,6 +219,9 @@ specify init my-project --ai shai
# Initialize with IBM Bob support
specify init my-project --ai bob

# Initialize with an unsupported agent (generic / bring your own agent)
specify init my-project --ai generic --ai-commands-dir .myagent/commands/

# Initialize with PowerShell scripts (Windows/cross-platform)
specify init my-project --ai copilot --script ps

Expand Down
5 changes: 4 additions & 1 deletion scripts/bash/update-agent-context.sh
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,12 @@ update_specific_agent() {
bob)
update_agent_file "$BOB_FILE" "IBM Bob"
;;
generic)
log_info "Generic agent: no predefined context file. Use the agent-specific update script for your agent."
;;
*)
log_error "Unknown agent type '$agent_type'"
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|shai|q|agy|bob|qoder"
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|shai|q|agy|bob|qoder|generic"
exit 1
;;
esac
Expand Down
7 changes: 4 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','shai','q','agy','bob','qoder')]
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','q','agy','bob','qoder','generic')]
[string]$AgentType
)

Expand Down Expand Up @@ -390,7 +390,8 @@ function Update-SpecificAgent {
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
'agy' { Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity' }
'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qoder'; return $false }
'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qoder|generic'; return $false }
}
}

Expand Down Expand Up @@ -427,7 +428,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|shai|q|agy|bob|qoder]'
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qoder|generic]'
}

function Main {
Expand Down
55 changes: 44 additions & 11 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str)
"install_url": None, # IDE-based
"requires_cli": False,
},
"generic": {
"name": "Generic (bring your own agent)",
"folder": None, # Set dynamically via --ai-commands-dir
"install_url": None,
"requires_cli": False,
},
}

SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
Expand Down Expand Up @@ -1188,7 +1194,8 @@ def install_ai_skills(project_path: Path, selected_ai: str, tracker: StepTracker
@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, shai, q, agy, bob, or qoder "),
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, shai, q, agy, bob, qoder, or generic (requires --ai-commands-dir)"),
ai_commands_dir: str = typer.Option(None, "--ai-commands-dir", help="Directory for agent command files (required with --ai generic, e.g. .myagent/commands/)"),
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"),
Expand Down Expand Up @@ -1224,6 +1231,7 @@ def init(
specify init --here --force # Skip confirmation when current directory not empty
specify init my-project --ai claude --ai-skills # Install agent skills
specify init --here --ai gemini --ai-skills
specify init my-project --ai generic --ai-commands-dir .myagent/commands/ # Unsupported agent
"""

show_banner()
Expand Down Expand Up @@ -1308,6 +1316,16 @@ def init(
"copilot"
)

# Validate --ai-commands-dir usage
if selected_ai == "generic":
if not ai_commands_dir:
console.print("[red]Error:[/red] --ai-commands-dir is required when using --ai generic")
console.print("[dim]Example: specify init my-project --ai generic --ai-commands-dir .myagent/commands/[/dim]")
raise typer.Exit(1)
elif ai_commands_dir:
console.print(f"[red]Error:[/red] --ai-commands-dir can only be used with --ai generic (not '{selected_ai}')")
raise typer.Exit(1)

if not ignore_agent_tools:
agent_config = AGENT_CONFIG.get(selected_ai)
if agent_config and agent_config["requires_cli"]:
Expand Down Expand Up @@ -1383,6 +1401,18 @@ def init(

download_and_extract_template(project_path, selected_ai, selected_script, here, verbose=False, tracker=tracker, client=local_client, debug=debug, github_token=github_token)

# For generic agent, rename placeholder directory to user-specified path
if selected_ai == "generic" and ai_commands_dir:
placeholder_dir = project_path / ".speckit" / "commands"
target_dir = project_path / ai_commands_dir
Comment on lines +1406 to +1407
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The directory renaming logic doesn't validate that ai_commands_dir is a relative path. If a user provides an absolute path (e.g., '/tmp/commands/'), the behavior of 'project_path / ai_commands_dir' could be unexpected. Consider adding validation to ensure ai_commands_dir is a relative path and doesn't contain '..' to prevent directory traversal or paths outside the project directory.

This issue also appears on line 1195 of the same file.

Suggested change
placeholder_dir = project_path / ".speckit" / "commands"
target_dir = project_path / ai_commands_dir
# Ensure ai_commands_dir is a safe, relative path inside the project directory
ai_path = Path(ai_commands_dir)
if ai_path.is_absolute() or ".." in ai_path.parts:
raise ValueError("ai_commands_dir must be a relative path without '..' components.")
project_root = project_path.resolve()
target_dir = (project_root / ai_path).resolve()
# Ensure the target directory is inside the project directory
if target_dir != project_root and project_root not in target_dir.parents:
raise ValueError("ai_commands_dir must point inside the project directory.")
placeholder_dir = project_path / ".speckit" / "commands"

Copilot uses AI. Check for mistakes.
if placeholder_dir.is_dir():
target_dir.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(placeholder_dir), str(target_dir))
# Clean up empty .speckit dir if it's now empty
speckit_dir = project_path / ".speckit"
if speckit_dir.is_dir() and not any(speckit_dir.iterdir()):
speckit_dir.rmdir()

Comment on lines 1319 to 1415
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new generic agent functionality, including --ai-commands-dir validation and directory renaming logic, lacks test coverage. Consider adding tests that verify: 1) --ai-commands-dir is required when using --ai generic, 2) --ai-commands-dir is rejected with other agents, 3) the placeholder directory is correctly renamed to the user-specified path, 4) proper cleanup of empty .speckit directory, and 5) edge cases like invalid paths or existing target directories.

This issue also appears on line 1100 of the same file.

Copilot uses AI. Check for mistakes.
ensure_executable_scripts(project_path, tracker=tracker)

ensure_constitution_from_template(project_path, tracker=tracker)
Expand Down Expand Up @@ -1468,16 +1498,17 @@ def init(
# Agent folder security notice
agent_config = AGENT_CONFIG.get(selected_ai)
if agent_config:
agent_folder = agent_config["folder"]
security_notice = Panel(
f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n"
f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
title="[yellow]Agent Folder Security[/yellow]",
border_style="yellow",
padding=(1, 2)
)
console.print()
console.print(security_notice)
agent_folder = ai_commands_dir if selected_ai == "generic" else agent_config["folder"]
if agent_folder:
security_notice = Panel(
f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n"
f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
title="[yellow]Agent Folder Security[/yellow]",
border_style="yellow",
padding=(1, 2)
)
console.print()
console.print(security_notice)

steps_lines = []
if not here:
Expand Down Expand Up @@ -1535,6 +1566,8 @@ def check():

agent_results = {}
for agent_key, agent_config in AGENT_CONFIG.items():
if agent_key == "generic":
continue # Generic is not a real agent to check
agent_name = agent_config["name"]
requires_cli = agent_config["requires_cli"]

Expand Down
2 changes: 1 addition & 1 deletion tests/test_ai_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def test_non_md_commands_dir_falls_back(self, project_dir):
# .toml commands should be untouched
assert (cmds_dir / "speckit.specify.toml").exists()

@pytest.mark.parametrize("agent_key", list(AGENT_CONFIG.keys()))
@pytest.mark.parametrize("agent_key", [k for k in AGENT_CONFIG.keys() if k != "generic"])
def test_skills_install_for_all_agents(self, temp_dir, agent_key):
"""install_ai_skills should produce skills for every configured agent."""
proj = temp_dir / f"proj-{agent_key}"
Expand Down