From 31e72f4030f5230e7248dc2ca00da311e49f9a7d Mon Sep 17 00:00:00 2001 From: Forge Date: Mon, 10 Aug 2026 10:41:22 +0000 Subject: [PATCH] [AISOS-2390] Add forge version CLI command Detailed description: - Implemented cmd_version async command handler in src/forge/cli.py to retrieve and print the installed Forge package version - Registered the version parser to subparsers and registered the command route mapping in src/forge/cli.py - Added comprehensive unit tests in tests/unit/test_cli_version.py covering CLI parsing, routing, output verification and exit codes - Updated CLI developer documentation in both CLAUDE.md and docs/developer-guide.md Closes: AISOS-2390 --- CLAUDE.md | 3 +++ docs/developer-guide.md | 10 ++++++++++ src/forge/cli.py | 15 +++++++++++++++ tests/unit/test_cli_version.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 tests/unit/test_cli_version.py diff --git a/CLAUDE.md b/CLAUDE.md index 3f852ad75..1eb1e8936 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,6 +55,9 @@ uv run uvicorn forge.main:app --reload --port 8000 --host 0.0.0.0 # Start queue worker uv run forge worker +# Print Forge version +uv run forge version + # Build container podman build -t forge-dev:latest containers/ ``` diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 9bf63a0e4..3c1ed17de 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -716,6 +716,16 @@ For workflows paused at `review_response_gate` (due to contested comments): - Reset `is_paused` and `is_blocked` to `False` - Set `force_fresh_invoke` to `True` to await a fresh human review +### Forge Version Command + +To print the currently installed Forge package version, run: + +```bash +uv run forge version +``` + +This will print the package version in the format `Forge v` (e.g., `Forge v0.1.0`) and exit with a success status code. + ### Worker logs The worker logs to stdout. Useful log entries to grep for: diff --git a/src/forge/cli.py b/src/forge/cli.py index b852edb10..0e58d1fa0 100644 --- a/src/forge/cli.py +++ b/src/forge/cli.py @@ -1205,6 +1205,14 @@ async def cmd_smoke_test(_args: argparse.Namespace) -> int: return await run_smoke_test(settings) +async def cmd_version(_args: argparse.Namespace) -> int: + """Print the installed Forge package version.""" + from forge import __version__ + + print(f"Forge v{__version__}") + return 0 + + def main(argv: list[str] | None = None) -> int: """Main CLI entry point.""" parser = argparse.ArgumentParser( @@ -1326,6 +1334,12 @@ def main(argv: list[str] | None = None) -> int: help="Run an end-to-end smoke test to verify Forge runtime connectivity and execution", ) + # version command + subparsers.add_parser( + "version", + help="Print the installed Forge package version", + ) + # skills subparser group skills_parser = subparsers.add_parser( "skills", @@ -1607,6 +1621,7 @@ def main(argv: list[str] | None = None) -> int: "retry": cmd_retry, "logs": cmd_logs, "smoke-test": cmd_smoke_test, + "version": cmd_version, "project-setup": cmd_project_setup, "get-config": cmd_get_config, } diff --git a/tests/unit/test_cli_version.py b/tests/unit/test_cli_version.py new file mode 100644 index 000000000..f37974593 --- /dev/null +++ b/tests/unit/test_cli_version.py @@ -0,0 +1,33 @@ +"""Unit tests for the forge version CLI command.""" + +import argparse +from unittest.mock import AsyncMock, patch + +import pytest + +from forge import __version__ +from forge.cli import cmd_version, main + + +class TestCLIVersionParserAndRouting: + """Parser routing and command execution tests for version.""" + + @patch("forge.cli.cmd_version", new_callable=AsyncMock) + @patch("forge.cli.setup_logging") + def test_routing_version(self, _mock_setup_logging, mock_cmd): + """Calling main(['version']) routes to cmd_version.""" + mock_cmd.return_value = 0 + code = main(["version"]) + assert code == 0 + mock_cmd.assert_called_once() + args = mock_cmd.call_args[0][0] + assert args.command == "version" + + @pytest.mark.asyncio + async def test_cmd_version_execution(self, capsys): + """cmd_version prints the correct version string and exits with 0.""" + args = argparse.Namespace() + code = await cmd_version(args) + assert code == 0 + captured = capsys.readouterr() + assert f"Forge v{__version__}" in captured.out