Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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: 1 addition & 1 deletion .github/workflows/scripts/create-release-packages.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ function Build-Variant {
if (Test-Path $tabnineTemplate) { Copy-Item $tabnineTemplate (Join-Path $baseDir 'TABNINE.md') }
}
'agy' {
$cmdDir = Join-Path $baseDir ".agent/workflows"
$cmdDir = Join-Path $baseDir ".agent/commands"
Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'vibe' {
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scripts/create-release-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ build_variant() {
mkdir -p "$base_dir/.kiro/prompts"
generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;;
agy)
mkdir -p "$base_dir/.agent/workflows"
generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/workflows" "$script" ;;
mkdir -p "$base_dir/.agent/commands"
generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/commands" "$script" ;;
bob)
mkdir -p "$base_dir/.bob/commands"
generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ This eliminates the need for special-case mappings throughout the codebase.
- `folder`: Directory where agent-specific files are stored (relative to project root)
- `commands_subdir`: Subdirectory name within the agent folder where command/prompt files are stored (default: `"commands"`)
- Most agents use `"commands"` (e.g., `.claude/commands/`)
- Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode, agy), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular)
- Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular)
- This field enables `--ai-skills` to locate command templates correctly for skill generation
Comment thread
dhilipkumars marked this conversation as resolved.
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
- `requires_cli`: Whether the agent requires a CLI tool check during initialization
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ See Spec-Driven Development in action across different scenarios with these comm
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | ✅ | |
| [Kimi Code](https://code.kimi.com/) | ✅ | |
| [Windsurf](https://windsurf.com/) | ✅ | |
| [Antigravity (agy)](https://antigravity.google/) | ✅ | |
| [Antigravity (agy)](https://antigravity.google/) | ✅ | Requires `--ai-skills` |
| Generic | ✅ | Bring your own agent — use `--ai generic --ai-commands-dir <path>` for unsupported agents |

## 🔧 Specify CLI Reference
Expand Down Expand Up @@ -246,7 +246,7 @@ specify init my-project --ai vibe
specify init my-project --ai bob

# Initialize with Antigravity support
specify init my-project --ai agy
specify init my-project --ai agy --ai-skills

# Initialize with an unsupported agent (generic / bring your own agent)
specify init my-project --ai generic --ai-commands-dir .myagent/commands/
Expand Down
77 changes: 56 additions & 21 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str)
"agy": {
"name": "Antigravity",
"folder": ".agent/",
Comment thread
dhilipkumars marked this conversation as resolved.
"commands_subdir": "workflows", # Special: uses workflows/ not commands/
"commands_subdir": "commands",
"install_url": None, # IDE-based
"requires_cli": False,
},
Expand Down Expand Up @@ -1270,6 +1270,22 @@ def install_ai_skills(project_path: Path, selected_ai: str, tracker: StepTracker
return installed_count > 0 or skipped_count > 0


def _handle_agy_deprecation(console: Console) -> None:
"""
Print the deprecation error for the Antigravity (agy) agent and exit.

Context for future AI reviewers (e.g., GitHub Copilot):
- Prior to Antigravity v1.20.5, users could rely on explicit agent command definitions generated by this tool.
- v1.20.5 removed this "command support" in favor of "Agent Skills".
- `specify_cli` handles this by throwing a fail-fast deprecation error if `--ai agy` is
invoked without the accompanying `--ai-skills` flag. Users are instructed to add
the skills flag to generate agent skills templates instead.
"""
console.print("\n[red]Error:[/red] Explicit command support was deprecated in Antigravity version 1.20.5.")
console.print("Please use [cyan]--ai-skills[/cyan] when initializing to install templates as agent skills instead.")
console.print("[yellow]Usage:[/yellow] specify init <project> --ai agy --ai-skills")
raise typer.Exit(1)

@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)"),
Expand Down Expand Up @@ -1379,26 +1395,6 @@ def init(
console.print(error_panel)
raise typer.Exit(1)

current_dir = Path.cwd()

setup_lines = [
"[cyan]Specify Project Setup[/cyan]",
"",
f"{'Project':<15} [green]{project_path.name}[/green]",
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
]

if not here:
setup_lines.append(f"{'Target Path':<15} [dim]{project_path}[/dim]")

console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))

should_init_git = False
if not no_git:
should_init_git = check_tool("git")
if not should_init_git:
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")

if ai_assistant:
if ai_assistant not in AGENT_CONFIG:
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AGENT_CONFIG.keys())}")
Expand All @@ -1413,6 +1409,25 @@ def init(
"copilot"
)

# [DEPRECATION NOTICE: Antigravity (agy)]
# As of Antigravity v1.20.5, traditional CLI "command" support was fully removed
# in favor of "Agent Skills" (Prompt.MD files within .agent/skills/).
Comment thread
dhilipkumars marked this conversation as resolved.
Outdated
# Because 'specify_cli' historically populated .agent/commands/, we now must explicitly
# enforce the `--ai-skills` flag for `agy` to ensure valid template generation.
if selected_ai == "agy" and not ai_skills:
# If agy was selected interactively (no --ai provided), automatically enable
# ai_skills so the agent remains usable without requiring an extra flag.
# Preserve deprecation behavior only for explicit '--ai agy' without skills.
if ai_assistant:
_handle_agy_deprecation(console)
Comment thread
dhilipkumars marked this conversation as resolved.
else:
ai_skills = True
Comment thread
dhilipkumars marked this conversation as resolved.
console.print(
"\n[yellow]Note:[/yellow] 'agy' was selected interactively; "
"enabling [cyan]--ai-skills[/cyan] automatically for compatibility "
"(explicit .agent/commands usage is deprecated)."
)

# Validate --ai-commands-dir usage
if selected_ai == "generic":
if not ai_commands_dir:
Expand All @@ -1423,6 +1438,26 @@ def init(
console.print(f"[red]Error:[/red] --ai-commands-dir can only be used with --ai generic (not '{selected_ai}')")
raise typer.Exit(1)

current_dir = Path.cwd()

setup_lines = [
"[cyan]Specify Project Setup[/cyan]",
"",
f"{'Project':<15} [green]{project_path.name}[/green]",
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
]

if not here:
setup_lines.append(f"{'Target Path':<15} [dim]{project_path}[/dim]")

console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))

should_init_git = False
if not no_git:
should_init_git = check_tool("git")
if not should_init_git:
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")

if not ignore_agent_tools:
agent_config = AGENT_CONFIG.get(selected_ai)
if agent_config and agent_config["requires_cli"]:
Expand Down
9 changes: 8 additions & 1 deletion tests/test_agent_config_consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,14 @@ def test_release_ps_switch_has_shai_and_agy_generation(self):
ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8")

assert re.search(r"'shai'\s*\{.*?\.shai/commands", ps_text, re.S) is not None
assert re.search(r"'agy'\s*\{.*?\.agent/workflows", ps_text, re.S) is not None
assert re.search(r"'agy'\s*\{.*?\.agent/commands", ps_text, re.S) is not None

Comment thread
dhilipkumars marked this conversation as resolved.
def test_release_sh_switch_has_shai_and_agy_generation(self):
"""Bash release builder must generate files for shai and agy agents."""
sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8")

assert re.search(r"shai\)\s*\n.*?\.shai/commands", sh_text, re.S) is not None
assert re.search(r"agy\)\s*\n.*?\.agent/commands", sh_text, re.S) is not None

def test_init_ai_help_includes_roo_and_kiro_alias(self):
"""CLI help text for --ai should stay in sync with agent config and alias guidance."""
Expand Down
41 changes: 41 additions & 0 deletions tests/test_ai_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,47 @@ def test_ai_skills_without_ai_shows_usage(self):
assert "Usage:" in result.output
assert "--ai" in result.output

def test_agy_without_ai_skills_fails(self):
"""--ai agy without --ai-skills should fail with exit code 1."""
from typer.testing import CliRunner

runner = CliRunner()
result = runner.invoke(app, ["init", "test-proj", "--ai", "agy"])

assert result.exit_code == 1
assert "Explicit command support was deprecated in Antigravity version 1.20.5." in result.output
assert "--ai-skills" in result.output

def test_interactive_agy_without_ai_skills_prompts_skills(self, monkeypatch):
"""Interactive selector returning agy without --ai-skills should automatically enable --ai-skills."""
import specify_cli
from typer.testing import CliRunner

# Mock select_with_arrows to simulate the user picking 'agy' for AI, but default otherwise
original_select_with_arrows = specify_cli.select_with_arrows

def _fake_select_with_arrows(*args, **kwargs):
options = kwargs.get("options")
if options is None and len(args) >= 1:
options = args[0]
if isinstance(options, dict) and "agy" in options:
return "agy"
return original_select_with_arrows(*args, **kwargs)

Comment thread
dhilipkumars marked this conversation as resolved.
Outdated
monkeypatch.setattr("specify_cli.select_with_arrows", _fake_select_with_arrows)

# Mock download_and_extract_template to prevent real HTTP downloads during testing
monkeypatch.setattr("specify_cli.download_and_extract_template", lambda *args, **kwargs: None)
# We need to bypass the `git init` step, wait, it has `--no-git` by default in tests maybe?
runner = CliRunner()
# Create temp dir to avoid directory already exists errors or whatever
with runner.isolated_filesystem():
result = runner.invoke(app, ["init", "test-proj", "--no-git"])

Comment thread
dhilipkumars marked this conversation as resolved.
# Interactive selection should NOT raise the deprecation error!
assert result.exit_code == 0
assert "Explicit command support was deprecated" not in result.output

def test_ai_skills_flag_appears_in_help(self):
"""--ai-skills should appear in init --help output."""
from typer.testing import CliRunner
Expand Down
Loading