diff --git a/lib/cli/src/crewai_cli/create_flow.py b/lib/cli/src/crewai_cli/create_flow.py index adaa3d3bff..24fee7af55 100644 --- a/lib/cli/src/crewai_cli/create_flow.py +++ b/lib/cli/src/crewai_cli/create_flow.py @@ -4,6 +4,8 @@ import click from crewai_core.telemetry import Telemetry +from crewai_cli.version import get_crewai_tools_dependency + DECLARATIVE_FLOW_FOLDERS = ("crews", "tools", "knowledge", "skills") @@ -71,6 +73,9 @@ def process_file(src_file: Path, dst_file: Path) -> None: content = content.replace("{{name}}", name) content = content.replace("{{flow_name}}", class_name) content = content.replace("{{folder_name}}", folder_name) + content = content.replace( + "{{crewai_tools_dependency}}", get_crewai_tools_dependency() + ) with open(dst_file, "w") as file: file.write(content) @@ -138,6 +143,9 @@ def _create_declarative_flow( content = content.replace("{{name}}", name) content = content.replace("{{flow_name}}", class_name) content = content.replace("{{folder_name}}", folder_name) + content = content.replace( + "{{crewai_tools_dependency}}", get_crewai_tools_dependency() + ) dst_file.write_text(content, encoding="utf-8") (project_root / ".env").write_text("OPENAI_API_KEY=YOUR_API_KEY", encoding="utf-8") diff --git a/lib/cli/src/crewai_cli/create_json_crew.py b/lib/cli/src/crewai_cli/create_json_crew.py index 9cc0c37879..ffc4090c23 100644 --- a/lib/cli/src/crewai_cli/create_json_crew.py +++ b/lib/cli/src/crewai_cli/create_json_crew.py @@ -20,6 +20,7 @@ load_env_vars, write_env_file, ) +from crewai_cli.version import get_crewai_tools_dependency # ── Provider / model data ─────────────────────────────────────── @@ -89,7 +90,7 @@ authors = [{{ name = "Your Name", email = "you@example.com" }}] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.14.8a1" + "{crewai_tools_dependency}" ] [build-system] @@ -1134,7 +1135,11 @@ def create_json_crew( # Write pyproject.toml (folder_path / "pyproject.toml").write_text( - _PYPROJECT_TOML.format(folder_name=folder_name, name=name), + _PYPROJECT_TOML.format( + folder_name=folder_name, + name=name, + crewai_tools_dependency=get_crewai_tools_dependency(), + ), encoding="utf-8", ) diff --git a/lib/cli/src/crewai_cli/run_crew.py b/lib/cli/src/crewai_cli/run_crew.py index de6c8c4120..281f6270b1 100644 --- a/lib/cli/src/crewai_cli/run_crew.py +++ b/lib/cli/src/crewai_cli/run_crew.py @@ -19,7 +19,7 @@ is_dmn_mode_enabled, read_toml, ) -from crewai_cli.version import get_crewai_version +from crewai_cli.version import get_crewai_tools_dependency, get_crewai_version if TYPE_CHECKING: @@ -32,12 +32,12 @@ _INPUT_PLACEHOLDER_RE = re.compile(r"(?=3.10,<3.14" dependencies = [ - "crewai[tools]==1.15.0" + "{{crewai_tools_dependency}}" ] [project.scripts] diff --git a/lib/cli/src/crewai_cli/templates/declarative_flow/pyproject.toml b/lib/cli/src/crewai_cli/templates/declarative_flow/pyproject.toml index c19f3a85c3..49498fb5a8 100644 --- a/lib/cli/src/crewai_cli/templates/declarative_flow/pyproject.toml +++ b/lib/cli/src/crewai_cli/templates/declarative_flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.15.0" + "{{crewai_tools_dependency}}" ] [build-system] diff --git a/lib/cli/src/crewai_cli/templates/flow/pyproject.toml b/lib/cli/src/crewai_cli/templates/flow/pyproject.toml index 24e32f91f2..12685b995a 100644 --- a/lib/cli/src/crewai_cli/templates/flow/pyproject.toml +++ b/lib/cli/src/crewai_cli/templates/flow/pyproject.toml @@ -5,7 +5,7 @@ description = "{{name}} using crewAI" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.15.0" + "{{crewai_tools_dependency}}" ] [project.scripts] diff --git a/lib/cli/src/crewai_cli/templates/tool/pyproject.toml b/lib/cli/src/crewai_cli/templates/tool/pyproject.toml index a994abfed6..2c466718d3 100644 --- a/lib/cli/src/crewai_cli/templates/tool/pyproject.toml +++ b/lib/cli/src/crewai_cli/templates/tool/pyproject.toml @@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}" readme = "README.md" requires-python = ">=3.10,<3.14" dependencies = [ - "crewai[tools]==1.15.0" + "{{crewai_tools_dependency}}" ] [tool.crewai] diff --git a/lib/cli/src/crewai_cli/tools/main.py b/lib/cli/src/crewai_cli/tools/main.py index 0c05f269d7..9917f097ac 100644 --- a/lib/cli/src/crewai_cli/tools/main.py +++ b/lib/cli/src/crewai_cli/tools/main.py @@ -23,6 +23,7 @@ tree_copy, tree_find_and_replace, ) +from crewai_cli.version import get_crewai_tools_dependency console = Console() @@ -81,6 +82,9 @@ def create(self, handle: str) -> None: tree_copy(template_dir, project_root) tree_find_and_replace(project_root, "{{folder_name}}", folder_name) tree_find_and_replace(project_root, "{{class_name}}", class_name) + tree_find_and_replace( + project_root, "{{crewai_tools_dependency}}", get_crewai_tools_dependency() + ) agents_md_src = Path(__file__).parent.parent / "templates" / "AGENTS.md" if agents_md_src.exists(): diff --git a/lib/cli/src/crewai_cli/utils.py b/lib/cli/src/crewai_cli/utils.py index 0834c18518..ee3a255a96 100644 --- a/lib/cli/src/crewai_cli/utils.py +++ b/lib/cli/src/crewai_cli/utils.py @@ -19,6 +19,8 @@ ) from rich.console import Console +from crewai_cli.version import get_crewai_tools_dependency + __all__ = [ "build_env_with_all_tool_credentials", @@ -73,6 +75,9 @@ def copy_template( content = content.replace("{{name}}", name) content = content.replace("{{crew_name}}", class_name) content = content.replace("{{folder_name}}", folder_name) + content = content.replace( + "{{crewai_tools_dependency}}", get_crewai_tools_dependency() + ) with open(dst, "w") as file: file.write(content) diff --git a/lib/cli/src/crewai_cli/version.py b/lib/cli/src/crewai_cli/version.py index cd9cc1d48e..ad751b33e7 100644 --- a/lib/cli/src/crewai_cli/version.py +++ b/lib/cli/src/crewai_cli/version.py @@ -13,10 +13,26 @@ is_current_version_yanked as is_current_version_yanked, is_newer_version_available as is_newer_version_available, ) +from packaging.version import Version + +from crewai_cli import __version__ as _crewai_cli_version + + +def get_crewai_dependency_range(current_version: str | None = None) -> str: + """Return the supported CrewAI dependency range for generated projects.""" + parsed_version = Version(current_version or _crewai_cli_version) + return f">={parsed_version},<{parsed_version.major + 1}.0.0" + + +def get_crewai_tools_dependency(current_version: str | None = None) -> str: + """Return the generated-project dependency for CrewAI with tools.""" + return f"crewai[tools]{get_crewai_dependency_range(current_version)}" __all__ = [ "check_version", + "get_crewai_dependency_range", + "get_crewai_tools_dependency", "get_crewai_version", "get_latest_version_from_pypi", "is_current_version_yanked", diff --git a/lib/cli/tests/deploy/test_archive.py b/lib/cli/tests/deploy/test_archive.py index dff77a29bb..56344dba2c 100644 --- a/lib/cli/tests/deploy/test_archive.py +++ b/lib/cli/tests/deploy/test_archive.py @@ -176,7 +176,7 @@ def test_create_project_zip_keeps_json_project_root_shape(tmp_path: Path): [project] name = "json_crew" version = "0.1.0" -dependencies = ["crewai[tools]==1.14.8a1"] +dependencies = ["crewai[tools]>=1.15.0,<2.0.0"] [tool.crewai] type = "crew" diff --git a/lib/cli/tests/test_create_crew.py b/lib/cli/tests/test_create_crew.py index 5d87f8f3e3..803dd5948a 100644 --- a/lib/cli/tests/test_create_crew.py +++ b/lib/cli/tests/test_create_crew.py @@ -735,8 +735,9 @@ def test_json_create_provider_preselects_default_model(tmp_path, monkeypatch): pyproject = tomli.loads((tmp_path / "json_crew" / "pyproject.toml").read_text()) dependency = pyproject["project"]["dependencies"][0] - assert dependency == "crewai[tools]==1.14.8a1" - assert Version("1.14.8a1") in Requirement(dependency).specifier + assert dependency == "crewai[tools]>=1.15.0,<2.0.0" + assert Version("1.15.0") in Requirement(dependency).specifier + assert Version("2.0.0") not in Requirement(dependency).specifier assert pyproject["tool"]["hatch"]["build"]["targets"]["wheel"][ "only-include" ] == ["agents", "crew.jsonc", "tools", "knowledge", "skills"] diff --git a/lib/cli/tests/test_run_crew.py b/lib/cli/tests/test_run_crew.py index 6db073919f..5b2e846b31 100644 --- a/lib/cli/tests/test_run_crew.py +++ b/lib/cli/tests/test_run_crew.py @@ -25,10 +25,7 @@ def missing_crewai_package(): message = exc_info.value.message assert "CrewAI CLI is installed without the `crewai` package" in message - assert ( - "uv tool install --force --prerelease=allow 'crewai[tools]==1.14.8a1'" - in message - ) + assert "uv tool install --force 'crewai[tools]>=1.15.0,<2.0.0'" in message assert "quotes are required in zsh" in message diff --git a/lib/cli/tests/test_version.py b/lib/cli/tests/test_version.py index 2d6d38eee4..58d224dec9 100644 --- a/lib/cli/tests/test_version.py +++ b/lib/cli/tests/test_version.py @@ -7,6 +7,8 @@ from crewai_cli.version import get_crewai_version as _get_ver from crewai_cli.version import ( + get_crewai_dependency_range, + get_crewai_tools_dependency, get_crewai_version, get_latest_version_from_pypi, is_current_version_yanked, @@ -31,6 +33,11 @@ def test_dynamic_versioning_consistency() -> None: assert len(package_version.strip()) > 0 +def test_generated_project_dependency_uses_next_major_upper_bound() -> None: + assert get_crewai_dependency_range("1.15.0") == ">=1.15.0,<2.0.0" + assert get_crewai_tools_dependency("1.15.0") == "crewai[tools]>=1.15.0,<2.0.0" + + class TestVersionChecking: """Test version checking utilities.""" diff --git a/lib/cli/tests/tools/test_main.py b/lib/cli/tests/tools/test_main.py index b8383cc0da..a103f01336 100644 --- a/lib/cli/tests/tools/test_main.py +++ b/lib/cli/tests/tools/test_main.py @@ -54,6 +54,10 @@ def test_create_success(mock_subprocess, capsys, tool_command): ) assert os.path.isfile(os.path.join("test_tool", "src", "test_tool", "tool.py")) + with open(os.path.join("test_tool", "pyproject.toml"), "r") as f: + content = f.read() + assert '"crewai[tools]>=1.15.0,<2.0.0"' in content + with open(os.path.join("test_tool", "src", "test_tool", "tool.py"), "r") as f: content = f.read() assert "class TestTool" in content