Skip to content

Commit 976c998

Browse files
authored
fix(cli): deprecate explicit command support for agy (#1798) (#1808)
* fix(cli): deprecate explicit command support for agy (#1798) * docs(cli): add tests and docs for agy deprecation (#1798) * fix: address review comments for agy deprecation * fix: address round 2 review comments for agy deprecation * fix: address round 3 review comments for agy deprecation * fix: address round 4 review comments for agy deprecation * fix: address round 5 review comments for agy deprecation * docs: add inline contextual comments to explain agy deprecation * docs: clarify historical context in agy deprecation docstring * fix: correct skills path in deprecation comment and make test mock fully deterministic
1 parent d3fc056 commit 976c998

7 files changed

Lines changed: 123 additions & 28 deletions

File tree

.github/workflows/scripts/create-release-packages.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ function Build-Variant {
442442
if (Test-Path $tabnineTemplate) { Copy-Item $tabnineTemplate (Join-Path $baseDir 'TABNINE.md') }
443443
}
444444
'agy' {
445-
$cmdDir = Join-Path $baseDir ".agent/workflows"
445+
$cmdDir = Join-Path $baseDir ".agent/commands"
446446
Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
447447
}
448448
'vibe' {

.github/workflows/scripts/create-release-packages.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ build_variant() {
280280
mkdir -p "$base_dir/.kiro/prompts"
281281
generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;;
282282
agy)
283-
mkdir -p "$base_dir/.agent/workflows"
284-
generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/workflows" "$script" ;;
283+
mkdir -p "$base_dir/.agent/commands"
284+
generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/commands" "$script" ;;
285285
bob)
286286
mkdir -p "$base_dir/.bob/commands"
287287
generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ This eliminates the need for special-case mappings throughout the codebase.
8888
- `folder`: Directory where agent-specific files are stored (relative to project root)
8989
- `commands_subdir`: Subdirectory name within the agent folder where command/prompt files are stored (default: `"commands"`)
9090
- Most agents use `"commands"` (e.g., `.claude/commands/`)
91-
- Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode, agy), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular)
91+
- Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular)
9292
- This field enables `--ai-skills` to locate command templates correctly for skill generation
9393
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
9494
- `requires_cli`: Whether the agent requires a CLI tool check during initialization

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ See Spec-Driven Development in action across different scenarios with these comm
183183
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) || |
184184
| [Kimi Code](https://code.kimi.com/) || |
185185
| [Windsurf](https://windsurf.com/) || |
186-
| [Antigravity (agy)](https://antigravity.google/) || |
186+
| [Antigravity (agy)](https://antigravity.google/) || Requires `--ai-skills` |
187187
| Generic || Bring your own agent — use `--ai generic --ai-commands-dir <path>` for unsupported agents |
188188

189189
## 🔧 Specify CLI Reference
@@ -248,7 +248,7 @@ specify init my-project --ai vibe
248248
specify init my-project --ai bob
249249

250250
# Initialize with Antigravity support
251-
specify init my-project --ai agy
251+
specify init my-project --ai agy --ai-skills
252252

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

src/specify_cli/__init__.py

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str)
247247
"agy": {
248248
"name": "Antigravity",
249249
"folder": ".agent/",
250-
"commands_subdir": "workflows", # Special: uses workflows/ not commands/
250+
"commands_subdir": "commands",
251251
"install_url": None, # IDE-based
252252
"requires_cli": False,
253253
},
@@ -1270,6 +1270,22 @@ def install_ai_skills(project_path: Path, selected_ai: str, tracker: StepTracker
12701270
return installed_count > 0 or skipped_count > 0
12711271

12721272

1273+
def _handle_agy_deprecation(console: Console) -> None:
1274+
"""
1275+
Print the deprecation error for the Antigravity (agy) agent and exit.
1276+
1277+
Context for future AI reviewers (e.g., GitHub Copilot):
1278+
- Prior to Antigravity v1.20.5, users could rely on explicit agent command definitions generated by this tool.
1279+
- v1.20.5 removed this "command support" in favor of "Agent Skills".
1280+
- `specify_cli` handles this by throwing a fail-fast deprecation error if `--ai agy` is
1281+
invoked without the accompanying `--ai-skills` flag. Users are instructed to add
1282+
the skills flag to generate agent skills templates instead.
1283+
"""
1284+
console.print("\n[red]Error:[/red] Explicit command support was deprecated in Antigravity version 1.20.5.")
1285+
console.print("Please use [cyan]--ai-skills[/cyan] when initializing to install templates as agent skills instead.")
1286+
console.print("[yellow]Usage:[/yellow] specify init <project> --ai agy --ai-skills")
1287+
raise typer.Exit(1)
1288+
12731289
@app.command()
12741290
def init(
12751291
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here, or use '.' for current directory)"),
@@ -1379,26 +1395,6 @@ def init(
13791395
console.print(error_panel)
13801396
raise typer.Exit(1)
13811397

1382-
current_dir = Path.cwd()
1383-
1384-
setup_lines = [
1385-
"[cyan]Specify Project Setup[/cyan]",
1386-
"",
1387-
f"{'Project':<15} [green]{project_path.name}[/green]",
1388-
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
1389-
]
1390-
1391-
if not here:
1392-
setup_lines.append(f"{'Target Path':<15} [dim]{project_path}[/dim]")
1393-
1394-
console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))
1395-
1396-
should_init_git = False
1397-
if not no_git:
1398-
should_init_git = check_tool("git")
1399-
if not should_init_git:
1400-
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
1401-
14021398
if ai_assistant:
14031399
if ai_assistant not in AGENT_CONFIG:
14041400
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AGENT_CONFIG.keys())}")
@@ -1413,6 +1409,25 @@ def init(
14131409
"copilot"
14141410
)
14151411

1412+
# [DEPRECATION NOTICE: Antigravity (agy)]
1413+
# As of Antigravity v1.20.5, traditional CLI "command" support was fully removed
1414+
# in favor of "Agent Skills" (SKILL.md files under <agent_folder>/skills/<skill_name>/).
1415+
# Because 'specify_cli' historically populated .agent/commands/, we now must explicitly
1416+
# enforce the `--ai-skills` flag for `agy` to ensure valid template generation.
1417+
if selected_ai == "agy" and not ai_skills:
1418+
# If agy was selected interactively (no --ai provided), automatically enable
1419+
# ai_skills so the agent remains usable without requiring an extra flag.
1420+
# Preserve deprecation behavior only for explicit '--ai agy' without skills.
1421+
if ai_assistant:
1422+
_handle_agy_deprecation(console)
1423+
else:
1424+
ai_skills = True
1425+
console.print(
1426+
"\n[yellow]Note:[/yellow] 'agy' was selected interactively; "
1427+
"enabling [cyan]--ai-skills[/cyan] automatically for compatibility "
1428+
"(explicit .agent/commands usage is deprecated)."
1429+
)
1430+
14161431
# Validate --ai-commands-dir usage
14171432
if selected_ai == "generic":
14181433
if not ai_commands_dir:
@@ -1423,6 +1438,26 @@ def init(
14231438
console.print(f"[red]Error:[/red] --ai-commands-dir can only be used with --ai generic (not '{selected_ai}')")
14241439
raise typer.Exit(1)
14251440

1441+
current_dir = Path.cwd()
1442+
1443+
setup_lines = [
1444+
"[cyan]Specify Project Setup[/cyan]",
1445+
"",
1446+
f"{'Project':<15} [green]{project_path.name}[/green]",
1447+
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
1448+
]
1449+
1450+
if not here:
1451+
setup_lines.append(f"{'Target Path':<15} [dim]{project_path}[/dim]")
1452+
1453+
console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))
1454+
1455+
should_init_git = False
1456+
if not no_git:
1457+
should_init_git = check_tool("git")
1458+
if not should_init_git:
1459+
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
1460+
14261461
if not ignore_agent_tools:
14271462
agent_config = AGENT_CONFIG.get(selected_ai)
14281463
if agent_config and agent_config["requires_cli"]:

tests/test_agent_config_consistency.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,14 @@ def test_release_ps_switch_has_shai_and_agy_generation(self):
6262
ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8")
6363

6464
assert re.search(r"'shai'\s*\{.*?\.shai/commands", ps_text, re.S) is not None
65-
assert re.search(r"'agy'\s*\{.*?\.agent/workflows", ps_text, re.S) is not None
65+
assert re.search(r"'agy'\s*\{.*?\.agent/commands", ps_text, re.S) is not None
66+
67+
def test_release_sh_switch_has_shai_and_agy_generation(self):
68+
"""Bash release builder must generate files for shai and agy agents."""
69+
sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8")
70+
71+
assert re.search(r"shai\)\s*\n.*?\.shai/commands", sh_text, re.S) is not None
72+
assert re.search(r"agy\)\s*\n.*?\.agent/commands", sh_text, re.S) is not None
6673

6774
def test_init_ai_help_includes_roo_and_kiro_alias(self):
6875
"""CLI help text for --ai should stay in sync with agent config and alias guidance."""

tests/test_ai_skills.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,59 @@ def test_ai_skills_without_ai_shows_usage(self):
661661
assert "Usage:" in result.output
662662
assert "--ai" in result.output
663663

664+
def test_agy_without_ai_skills_fails(self):
665+
"""--ai agy without --ai-skills should fail with exit code 1."""
666+
from typer.testing import CliRunner
667+
668+
runner = CliRunner()
669+
result = runner.invoke(app, ["init", "test-proj", "--ai", "agy"])
670+
671+
assert result.exit_code == 1
672+
assert "Explicit command support was deprecated in Antigravity version 1.20.5." in result.output
673+
assert "--ai-skills" in result.output
674+
675+
def test_interactive_agy_without_ai_skills_prompts_skills(self, monkeypatch):
676+
"""Interactive selector returning agy without --ai-skills should automatically enable --ai-skills."""
677+
from typer.testing import CliRunner
678+
679+
# Mock select_with_arrows to simulate the user picking 'agy' for AI,
680+
# and return a deterministic default for any other prompts to avoid
681+
# calling the real interactive implementation.
682+
def _fake_select_with_arrows(*args, **kwargs):
683+
options = kwargs.get("options")
684+
if options is None and len(args) >= 1:
685+
options = args[0]
686+
687+
# If the options include 'agy', simulate selecting it.
688+
if isinstance(options, dict) and "agy" in options:
689+
return "agy"
690+
if isinstance(options, (list, tuple)) and "agy" in options:
691+
return "agy"
692+
693+
# For any other prompt, return a deterministic, non-interactive default:
694+
# pick the first option if available.
695+
if isinstance(options, dict) and options:
696+
return next(iter(options.keys()))
697+
if isinstance(options, (list, tuple)) and options:
698+
return options[0]
699+
700+
# If no options are provided, fall back to None (should not occur in normal use).
701+
return None
702+
703+
monkeypatch.setattr("specify_cli.select_with_arrows", _fake_select_with_arrows)
704+
705+
# Mock download_and_extract_template to prevent real HTTP downloads during testing
706+
monkeypatch.setattr("specify_cli.download_and_extract_template", lambda *args, **kwargs: None)
707+
# We need to bypass the `git init` step, wait, it has `--no-git` by default in tests maybe?
708+
runner = CliRunner()
709+
# Create temp dir to avoid directory already exists errors or whatever
710+
with runner.isolated_filesystem():
711+
result = runner.invoke(app, ["init", "test-proj", "--no-git"])
712+
713+
# Interactive selection should NOT raise the deprecation error!
714+
assert result.exit_code == 0
715+
assert "Explicit command support was deprecated" not in result.output
716+
664717
def test_ai_skills_flag_appears_in_help(self):
665718
"""--ai-skills should appear in init --help output."""
666719
from typer.testing import CliRunner

0 commit comments

Comments
 (0)