Skip to content

Commit 39c7b04

Browse files
authored
chore: deprecate --ai flag in favor of --integration on specify init (#2218)
* 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 * Address PR review feedback for issue #2169 - Use existing strip_ansi helper from conftest instead of duplicating ANSI escape pattern - Properly escape ai_commands_dir with shlex.quote() to handle paths with spaces - Add shlex import to support proper command-line argument escaping
1 parent 3467d26 commit 39c7b04

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

src/specify_cli/__init__.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import json
3434
import json5
3535
import stat
36+
import shlex
3637
import yaml
3738
from pathlib import Path
3839
from typing import Any, Optional
@@ -92,6 +93,36 @@ def _build_ai_assistant_help() -> str:
9293
return base_help + " Use " + aliases_text + "."
9394
AI_ASSISTANT_HELP = _build_ai_assistant_help()
9495

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

97128
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
@@ -957,6 +988,7 @@ def init(
957988
"""
958989

959990
show_banner()
991+
ai_deprecation_warning: str | None = None
960992

961993
# Detect when option values are likely misinterpreted flags (parameter ordering issue)
962994
if ai_assistant and ai_assistant.startswith("--"):
@@ -995,6 +1027,10 @@ def init(
9951027
if not resolved_integration:
9961028
console.print(f"[red]Error:[/red] Unknown agent '{ai_assistant}'. Choose from: {', '.join(sorted(INTEGRATION_REGISTRY))}")
9971029
raise typer.Exit(1)
1030+
ai_deprecation_warning = _build_ai_deprecation_warning(
1031+
resolved_integration.key,
1032+
ai_commands_dir=ai_commands_dir,
1033+
)
9981034

9991035
# Deprecation warnings for --ai-skills and --ai-commands-dir (only when
10001036
# an integration has been resolved from --ai or --integration)
@@ -1428,6 +1464,16 @@ def init(
14281464
console.print()
14291465
console.print(security_notice)
14301466

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

tests/integrations/test_cli.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55

66
import yaml
77

8+
from tests.conftest import strip_ansi
9+
10+
11+
def _normalize_cli_output(output: str) -> str:
12+
output = strip_ansi(output)
13+
output = " ".join(output.split())
14+
return output.strip()
15+
816

917
class TestInitIntegrationFlag:
1018
def test_integration_and_ai_mutually_exclusive(self, tmp_path):
@@ -77,6 +85,59 @@ def test_ai_copilot_auto_promotes(self, tmp_path):
7785
assert result.exit_code == 0
7886
assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists()
7987

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

0 commit comments

Comments
 (0)