diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 0bbf42ad5..e360812c4 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -1211,6 +1211,7 @@ def init( ensure_constitution_from_template(project_path, tracker=tracker) + _git_ext_freshly_installed = False if not no_git: tracker.start("git") git_messages = [] @@ -1245,6 +1246,7 @@ def init( bundled_path, get_speckit_version() ) git_messages.append("extension installed") + _git_ext_freshly_installed = True else: git_has_error = True git_messages.append("bundled extension not found") @@ -1356,6 +1358,21 @@ def init( console.print(tracker.render()) console.print("\n[bold green]Project ready.[/bold green]") + if _git_ext_freshly_installed: + console.print() + console.print( + Panel( + "The [bold]git[/bold] extension is currently enabled by default, " + "but starting with [bold]v1.0.0[/bold] it will require explicit opt-in.\n\n" + "To opt in after v1.0.0:\n" + " • [cyan]specify init --extension git[/cyan]\n" + " • [cyan]specify extension add git[/cyan] (post-init)", + title="[yellow]⚠ Upcoming Change: git Extension[/yellow]", + border_style="yellow", + padding=(1, 2), + ) + ) + # Agent folder security notice agent_config = AGENT_CONFIG.get(selected_ai) if agent_config: diff --git a/tests/extensions/git/test_git_extension.py b/tests/extensions/git/test_git_extension.py index 098caf53b..f82b17fb0 100644 --- a/tests/extensions/git/test_git_extension.py +++ b/tests/extensions/git/test_git_extension.py @@ -587,3 +587,79 @@ def test_check_feature_branch_rejects_malformed_timestamp(self, tmp_path: Path): capture_output=True, text=True, ) assert result.returncode != 0 + + +# ── Deprecation Notice Tests ────────────────────────────────────────────────── + + +class TestGitExtDeprecationNotice: + """Tests for the v1.0.0 deprecation notice shown during specify init.""" + + def test_deprecation_notice_shown_on_fresh_install(self, tmp_path: Path): + """specify init shows the git extension deprecation notice on first install.""" + from typer.testing import CliRunner + from unittest.mock import patch, MagicMock + from specify_cli import app + + project_dir = tmp_path / "test-project" + runner = CliRunner() + + mock_registry = MagicMock() + mock_registry.is_installed.return_value = False + + mock_manager = MagicMock() + mock_manager.registry = mock_registry + + with patch("specify_cli.extensions.ExtensionManager", return_value=mock_manager): + result = runner.invoke( + app, + ["init", str(project_dir), "--ai", "claude", "--ignore-agent-tools", "--script", "sh"], + catch_exceptions=False, + ) + + assert result.exit_code == 0, result.output + assert "Upcoming Change: git Extension" in result.output + assert "v1.0.0" in result.output + assert "specify init --extension git" in result.output + + def test_deprecation_notice_not_shown_when_already_installed(self, tmp_path: Path): + """specify init does NOT show the deprecation notice when git extension is already installed.""" + from typer.testing import CliRunner + from unittest.mock import patch, MagicMock + from specify_cli import app + + project_dir = tmp_path / "test-project" + runner = CliRunner() + + mock_registry = MagicMock() + mock_registry.is_installed.return_value = True + + mock_manager = MagicMock() + mock_manager.registry = mock_registry + + with patch("specify_cli.extensions.ExtensionManager", return_value=mock_manager): + result = runner.invoke( + app, + ["init", str(project_dir), "--ai", "claude", "--ignore-agent-tools", "--script", "sh"], + catch_exceptions=False, + ) + + assert result.exit_code == 0, result.output + assert "Upcoming Change: git Extension" not in result.output + + def test_deprecation_notice_not_shown_with_no_git_flag(self, tmp_path: Path): + """specify init does NOT show the deprecation notice when --no-git is passed.""" + from typer.testing import CliRunner + from specify_cli import app + + project_dir = tmp_path / "test-project" + runner = CliRunner() + + result = runner.invoke( + app, + ["init", str(project_dir), "--ai", "claude", "--ignore-agent-tools", "--no-git", "--script", "sh"], + catch_exceptions=False, + ) + + assert result.exit_code == 0, result.output + assert "Upcoming Change: git Extension" not in result.output