Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/
```
Expand Down
10 changes: 10 additions & 0 deletions docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<version>` (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:
Expand Down
15 changes: 15 additions & 0 deletions src/forge/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
}
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/test_cli_version.py
Original file line number Diff line number Diff line change
@@ -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
Loading