From ee3dda68fdb5ed316d61975ebb38431d122b2694 Mon Sep 17 00:00:00 2001 From: chelsealong Date: Sun, 25 Oct 2026 00:15:34 +0000 Subject: [PATCH] fix(bundles): honor bundle integration during init --- src/specify_cli/bundles/command_init.py | 7 +++ .../specify_cli/bundles/test_command_init.py | 47 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/specify_cli/bundles/command_init.py b/src/specify_cli/bundles/command_init.py index c61dd53615..611ff5ad0d 100644 --- a/src/specify_cli/bundles/command_init.py +++ b/src/specify_cli/bundles/command_init.py @@ -30,6 +30,13 @@ def bundle_init( try: project_root = find_project_root() if project_root is None: + if bundle: + # Install resolves and validates the manifest before initializing, + # including the bundle-declared integration. + bundle_install( + bundle, integration=integration, offline=offline, refresh=False + ) + return init_integration = _resolve_init_integration(integration, None) console.print( f"[cyan]Initializing a Spec Kit project with integration " diff --git a/tests/specify_cli/bundles/test_command_init.py b/tests/specify_cli/bundles/test_command_init.py index 7d692b23fb..7537da8cec 100644 --- a/tests/specify_cli/bundles/test_command_init.py +++ b/tests/specify_cli/bundles/test_command_init.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json from pathlib import Path from unittest.mock import Mock @@ -8,12 +9,47 @@ from typer.testing import CliRunner from specify_cli import app -from tests.specify_cli.bundles.helpers import write_manifest +from tests.specify_cli.bundles.helpers import valid_manifest_dict, write_manifest runner = CliRunner() REPO_ROOT = Path(__file__).parents[3] +def test_bundle_init_uses_declared_integration_in_new_project(tmp_path, monkeypatch): + bundle = write_manifest( + tmp_path / "claude-bundle", + valid_manifest_dict(provides={}, integration={"id": "claude"}), + ) + project = tmp_path / "project" + project.mkdir() + monkeypatch.chdir(project) + + result = runner.invoke(app, ["bundle", "init", str(bundle), "--offline"]) + + assert result.exit_code == 0, result.output + state = json.loads((project / ".specify/integration.json").read_text()) + assert state["default_integration"] == "claude" + assert (project / ".specify/bundle-records.json").is_file() + + +def test_bundle_init_rejects_incompatible_bundle_before_initializing( + tmp_path, + monkeypatch, +): + bundle = write_manifest( + tmp_path / "incompatible-bundle", + valid_manifest_dict(provides={}, requires={"speckit_version": ">=9999.0.0"}), + ) + project = tmp_path / "project" + project.mkdir() + monkeypatch.chdir(project) + + result = runner.invoke(app, ["bundle", "init", str(bundle), "--offline"]) + + assert result.exit_code == 1, result.output + assert not (project / ".specify").exists() + + def _make_project(tmp_path: Path, name: str) -> Path: project = tmp_path / name (project / ".specify").mkdir(parents=True) @@ -33,7 +69,10 @@ def test_bundle_init_delegates_without_refresh(project, monkeypatch, bundle): install.assert_not_called() else: install.assert_called_once_with( - bundle, integration=None, offline=True, refresh=False, + bundle, + integration=None, + offline=True, + refresh=False, ) @@ -42,7 +81,9 @@ def test_bundle_init_delegates_without_refresh(project, monkeypatch, bundle): [("init", False), ("install", False), ("install", True)], ) def test_bundle_init_preserves_workflow_unless_explicitly_refreshed( - project, command, refresh, + project, + command, + refresh, ): bundle = REPO_ROOT / "bundles" / "bugfix" initial = runner.invoke(app, ["bundle", "init", str(bundle), "--offline"])