Skip to content

Commit fa5c530

Browse files
committed
chore: deprecate --ai flag in favor of --integration on specify init
- Adds deprecation warning when --ai is used - Shows equivalent --integration command replacement - Handles generic integration with --commands-dir mapping - Adds comprehensive test coverage for deprecation behavior - Warning displays as prominent red panel above Next Steps - --ai flag continues to function (non-breaking change) Fixes #2169
1 parent 3467d26 commit fa5c530

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

src/specify_cli/__init__.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,36 @@ def _build_ai_assistant_help() -> str:
9292
return base_help + " Use " + aliases_text + "."
9393
AI_ASSISTANT_HELP = _build_ai_assistant_help()
9494

95+
96+
def _build_integration_equivalent(
97+
integration_key: str,
98+
ai_commands_dir: str | None = None,
99+
) -> str:
100+
"""Build the modern --integration equivalent for legacy --ai usage."""
101+
102+
parts = [f"--integration {integration_key}"]
103+
if integration_key == "generic" and ai_commands_dir:
104+
parts.append(
105+
f'--integration-options="--commands-dir {ai_commands_dir}"'
106+
)
107+
return " ".join(parts)
108+
109+
110+
def _build_ai_deprecation_warning(
111+
integration_key: str,
112+
ai_commands_dir: str | None = None,
113+
) -> str:
114+
"""Build the legacy --ai deprecation warning message."""
115+
116+
replacement = _build_integration_equivalent(
117+
integration_key,
118+
ai_commands_dir=ai_commands_dir,
119+
)
120+
return (
121+
"[bold]--ai[/bold] is deprecated and will no longer be available in version 1.0.0 or later.\n\n"
122+
f"Use [bold]{replacement}[/bold] instead."
123+
)
124+
95125
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
96126

97127
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
@@ -957,6 +987,7 @@ def init(
957987
"""
958988

959989
show_banner()
990+
ai_deprecation_warning: str | None = None
960991

961992
# Detect when option values are likely misinterpreted flags (parameter ordering issue)
962993
if ai_assistant and ai_assistant.startswith("--"):
@@ -995,6 +1026,10 @@ def init(
9951026
if not resolved_integration:
9961027
console.print(f"[red]Error:[/red] Unknown agent '{ai_assistant}'. Choose from: {', '.join(sorted(INTEGRATION_REGISTRY))}")
9971028
raise typer.Exit(1)
1029+
ai_deprecation_warning = _build_ai_deprecation_warning(
1030+
resolved_integration.key,
1031+
ai_commands_dir=ai_commands_dir,
1032+
)
9981033

9991034
# Deprecation warnings for --ai-skills and --ai-commands-dir (only when
10001035
# an integration has been resolved from --ai or --integration)
@@ -1428,6 +1463,16 @@ def init(
14281463
console.print()
14291464
console.print(security_notice)
14301465

1466+
if ai_deprecation_warning:
1467+
deprecation_notice = Panel(
1468+
ai_deprecation_warning,
1469+
title="[bold red]Deprecation Warning[/bold red]",
1470+
border_style="red",
1471+
padding=(1, 2),
1472+
)
1473+
console.print()
1474+
console.print(deprecation_notice)
1475+
14311476
steps_lines = []
14321477
if not here:
14331478
steps_lines.append(f"1. Go to the project folder: [cyan]cd {project_name}[/cyan]")

tests/integrations/test_cli.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22

33
import json
44
import os
5+
import re
56

67
import yaml
78

89

10+
def _normalize_cli_output(output: str) -> str:
11+
output = re.sub(r"\x1b\[[0-9;]*m", "", output)
12+
output = re.sub(r"\s+", " ", output)
13+
return output.strip()
14+
15+
916
class TestInitIntegrationFlag:
1017
def test_integration_and_ai_mutually_exclusive(self, tmp_path):
1118
from typer.testing import CliRunner
@@ -77,6 +84,59 @@ def test_ai_copilot_auto_promotes(self, tmp_path):
7784
assert result.exit_code == 0
7885
assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists()
7986

87+
def test_ai_emits_deprecation_warning_with_integration_replacement(self, tmp_path):
88+
from typer.testing import CliRunner
89+
from specify_cli import app
90+
91+
project = tmp_path / "warn-ai"
92+
project.mkdir()
93+
old_cwd = os.getcwd()
94+
try:
95+
os.chdir(project)
96+
runner = CliRunner()
97+
result = runner.invoke(app, [
98+
"init", "--here", "--ai", "copilot", "--script", "sh", "--no-git",
99+
], catch_exceptions=False)
100+
finally:
101+
os.chdir(old_cwd)
102+
103+
normalized_output = _normalize_cli_output(result.output)
104+
assert result.exit_code == 0, result.output
105+
assert "Deprecation Warning" in normalized_output
106+
assert "--ai" in normalized_output
107+
assert "deprecated" in normalized_output
108+
assert "no longer be available" in normalized_output
109+
assert "1.0.0" in normalized_output
110+
assert "--integration copilot" in normalized_output
111+
assert normalized_output.index("Deprecation Warning") < normalized_output.index("Next Steps")
112+
assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists()
113+
114+
def test_ai_generic_warning_suggests_integration_options_equivalent(self, tmp_path):
115+
from typer.testing import CliRunner
116+
from specify_cli import app
117+
118+
project = tmp_path / "warn-generic"
119+
project.mkdir()
120+
old_cwd = os.getcwd()
121+
try:
122+
os.chdir(project)
123+
runner = CliRunner()
124+
result = runner.invoke(app, [
125+
"init", "--here", "--ai", "generic", "--ai-commands-dir", ".myagent/commands",
126+
"--script", "sh", "--no-git",
127+
], catch_exceptions=False)
128+
finally:
129+
os.chdir(old_cwd)
130+
131+
normalized_output = _normalize_cli_output(result.output)
132+
assert result.exit_code == 0, result.output
133+
assert "Deprecation Warning" in normalized_output
134+
assert "--integration generic" in normalized_output
135+
assert "--integration-options" in normalized_output
136+
assert ".myagent/commands" in normalized_output
137+
assert normalized_output.index("Deprecation Warning") < normalized_output.index("Next Steps")
138+
assert (project / ".myagent" / "commands" / "speckit.plan.md").exists()
139+
80140
def test_ai_claude_here_preserves_preexisting_commands(self, tmp_path):
81141
from typer.testing import CliRunner
82142
from specify_cli import app

0 commit comments

Comments
 (0)