diff --git a/docs/reference/core.md b/docs/reference/core.md index 0b7b5b49bc..5623c66ead 100644 --- a/docs/reference/core.md +++ b/docs/reference/core.md @@ -77,6 +77,16 @@ specify version Displays the Spec Kit CLI version, Python version, platform, and architecture. +To inspect local CLI capabilities without checking the network: + +```bash +specify version --features +specify version --features --json +``` + +The JSON form is intended for scripts and coding agents that need to choose a +workflow based on the installed CLI's supported features. + A quick version check is also available via: ```bash diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index d4e8632215..07037216d3 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -1155,15 +1155,59 @@ def check(): if not any(agent_results.values()): console.print("[dim]Tip: Install a coding agent for the best experience[/dim]") + +def _feature_capabilities() -> dict[str, bool]: + """Return stable local CLI capability flags for humans and agents.""" + return { + "controlled_multi_install_integrations": True, + "integration_use_command": True, + "multi_install_safe_registry_metadata": True, + "integration_upgrade_command": True, + "self_check_command": True, + "workflow_catalog": True, + "bundled_templates": True, + } + + @app.command() -def version(): +def version( + features: bool = typer.Option( + False, + "--features", + help="Show local CLI feature capabilities.", + ), + json_output: bool = typer.Option( + False, + "--json", + help="Emit feature capabilities as JSON. Requires --features.", + ), +): """Display version and system information.""" import platform - show_banner() - cli_version = get_speckit_version() + if json_output and not features: + console.print("[red]Error:[/red] --json requires --features.") + raise typer.Exit(1) + + if features: + capabilities = _feature_capabilities() + if json_output: + payload = {"version": cli_version, "features": capabilities} + console.print(json.dumps(payload, indent=2)) + return + + console.print(f"Spec Kit CLI: {cli_version}") + console.print() + console.print("Features:") + for key, enabled in capabilities.items(): + label = key.replace("_", " ") + console.print(f"- {label}: {'yes' if enabled else 'no'}") + return + + show_banner() + info_table = Table(show_header=False, box=None, padding=(0, 2)) info_table.add_column("Key", style="cyan", justify="right") info_table.add_column("Value", style="white") diff --git a/tests/test_cli_version.py b/tests/test_cli_version.py index 80555d8b77..041ff62e55 100644 --- a/tests/test_cli_version.py +++ b/tests/test_cli_version.py @@ -1,5 +1,6 @@ -"""Tests for the --version CLI flag.""" +"""Tests for CLI version reporting.""" +import json from unittest.mock import patch from typer.testing import CliRunner @@ -33,3 +34,46 @@ def test_version_flag_takes_precedence_over_subcommand(self): result = runner.invoke(app, ["--version", "init"]) assert result.exit_code == 0 assert "specify 0.7.2" in result.output + + +class TestVersionCommand: + """Test the `specify version` subcommand.""" + + def test_version_features_text(self): + """specify version --features prints local capability flags.""" + with patch("specify_cli.get_speckit_version", return_value="1.2.3"): + result = runner.invoke(app, ["version", "--features"]) + + assert result.exit_code == 0 + assert "Spec Kit CLI: 1.2.3" in result.output + assert "Features:" in result.output + assert "- controlled multi install integrations: yes" in result.output + assert "- integration use command: yes" in result.output + assert "- self check command: yes" in result.output + + def test_version_features_json(self): + """specify version --features --json prints machine-readable capabilities.""" + with patch("specify_cli.get_speckit_version", return_value="1.2.3"): + result = runner.invoke(app, ["version", "--features", "--json"]) + + assert result.exit_code == 0 + payload = json.loads(result.output) + assert payload == { + "version": "1.2.3", + "features": { + "controlled_multi_install_integrations": True, + "integration_use_command": True, + "multi_install_safe_registry_metadata": True, + "integration_upgrade_command": True, + "self_check_command": True, + "workflow_catalog": True, + "bundled_templates": True, + }, + } + + def test_version_json_requires_features(self): + """specify version --json is rejected until a JSON surface exists.""" + result = runner.invoke(app, ["version", "--json"]) + + assert result.exit_code != 0 + assert "--json requires --features" in result.output