Skip to content
Open
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
29 changes: 28 additions & 1 deletion src/pieces/command_interface/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ def get_examples(self):
"pieces config --editor nvim", "Set Neovim as default editor"
).example("pieces config --editor vim", "Set Vim as default editor")

builder.section(
header="PiecesOS Launch:",
command_template="pieces config --auto-launch-pieces-os",
).example(
"pieces config --auto-launch-pieces-os",
"Enable automatic PiecesOS startup when a command needs it",
).example(
"pieces config --no-auto-launch-pieces-os",
"Disable automatic PiecesOS startup",
)

return builder.build()

def get_docs(self) -> str:
Expand All @@ -52,15 +63,31 @@ def add_arguments(self, parser: argparse.ArgumentParser):
type=str,
help="Set the default code editor",
)
parser.add_argument(
"--auto-launch-pieces-os",
dest="auto_launch_pieces_os",
action=argparse.BooleanOptionalAction,
default=None,
help="Automatically launch PiecesOS when a command requires it",
)

def execute(self, **kwargs) -> int:
"""Execute the config command."""

editor = kwargs.get("editor")
auto_launch = kwargs.get("auto_launch_pieces_os")
if editor:
Settings.cli_config.editor = editor
Settings.logger.print(f"Editor set to: {editor}")
else:
if auto_launch is not None:
Settings.cli_config.auto_launch_pieces_os = auto_launch
status = "Enabled" if auto_launch else "Disabled"
Settings.logger.print(f"Auto-launch PiecesOS: {status}")
if editor is None and auto_launch is None:
Settings.logger.print("Current configuration:")
Settings.logger.print(f"Editor: {Settings.cli_config.editor or 'Not set'}")
Settings.logger.print(
"Auto-launch PiecesOS: "
+ ("Enabled" if Settings.cli_config.auto_launch_pieces_os else "Disabled")
)
return 0
11 changes: 11 additions & 0 deletions src/pieces/config/managers/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,14 @@ def theme(self, value: str) -> None:
self.config.theme = value
self.save()

@property
def auto_launch_pieces_os(self) -> bool:
"""Get whether the CLI should auto-launch PiecesOS."""
return self.config.auto_launch_pieces_os

@auto_launch_pieces_os.setter
def auto_launch_pieces_os(self, value: bool) -> None:
"""Set whether the CLI should auto-launch PiecesOS."""
self.config.auto_launch_pieces_os = value
self.save()

6 changes: 5 additions & 1 deletion src/pieces/config/schemas/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class CLIConfigSchema(BaseModel):
)
editor: Optional[str] = Field(default=None, description="Default editor command")
theme: str = Field(default="pieces-dark", description="TUI theme preference")
auto_launch_pieces_os: bool = Field(
default=True,
description="Automatically launch PiecesOS when a CLI command requires it",
)

@field_validator("editor")
@classmethod
Expand All @@ -27,4 +31,4 @@ def validate_editor(cls, v):
@field_validator('schema_version', mode='before')
@classmethod
def validate_semver_field(cls, v):
return validate_semver(v)
return validate_semver(v)
8 changes: 7 additions & 1 deletion src/pieces/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,14 @@ def startup(cls, bypass_login=False):
os_id = cls.get_os_id()
sentry_sdk.set_user({"id": os_id or "unknown"})
else:
if cls.pieces_client.is_pieces_running() or cls.open_pieces_widget():
if cls.cli_config.auto_launch_pieces_os and cls.open_pieces_widget():
return cls.startup(bypass_login)
if not cls.cli_config.auto_launch_pieces_os:
cls.logger.print(
"PiecesOS is required but isn't running.\n"
"Start it manually or enable auto-launch with `pieces config --auto-launch-pieces-os`."
)
sys.exit(2)
if cls.logger.confirm(
"Pieces OS is required but wasn’t found or couldn’t be launched.\n"
"Do you want to install it now and get started?"
Expand Down
82 changes: 82 additions & 0 deletions tests/config_command_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import argparse
import pytest
from unittest.mock import patch

from pieces.command_interface.config_command import ConfigCommand
from pieces.config.managers.cli import CLIManager
from pieces.settings import Settings


REAL_SETTINGS_STARTUP = Settings.startup.__func__


class TestConfigCommand:
@pytest.fixture
def config_command(self):
return ConfigCommand()

@patch.object(Settings, "logger")
def test_execute_persists_auto_launch_pieces_os(
self, mock_logger, config_command, tmp_path
):
cli_config = CLIManager(tmp_path / "cli.json")

with patch.object(Settings, "cli_config", cli_config):
result = config_command.execute(auto_launch_pieces_os=False)

assert result == 0
reloaded_config = CLIManager(tmp_path / "cli.json")
assert reloaded_config.auto_launch_pieces_os is False
mock_logger.print.assert_called_once()
assert "auto-launch" in mock_logger.print.call_args[0][0].lower()

@patch.object(Settings, "logger")
def test_execute_updates_editor_and_auto_launch_together(
self, mock_logger, config_command, tmp_path
):
cli_config = CLIManager(tmp_path / "cli.json")

with patch.object(Settings, "cli_config", cli_config):
result = config_command.execute(
editor="vim", auto_launch_pieces_os=False
)

assert result == 0
reloaded_config = CLIManager(tmp_path / "cli.json")
assert reloaded_config.editor == "vim"
assert reloaded_config.auto_launch_pieces_os is False
assert mock_logger.print.call_count == 2

def test_add_arguments_supports_no_auto_launch_flag(self, config_command):
parser = argparse.ArgumentParser()
config_command.add_arguments(parser)

args = parser.parse_args(["--no-auto-launch-pieces-os"])

assert args.auto_launch_pieces_os is False


class TestSettingsStartup:
@patch("sys.exit", side_effect=SystemExit(2))
@patch.object(Settings, "logger")
@patch.object(Settings, "cli_config")
@patch.object(Settings, "pieces_client")
@patch.object(Settings, "open_pieces_widget")
def test_startup_skips_launch_when_auto_launch_disabled(
self,
mock_open_pieces_widget,
mock_pieces_client,
mock_cli_config,
mock_logger,
mock_sys_exit,
):
mock_cli_config.auto_launch_pieces_os = False
mock_pieces_client.is_pieces_running.return_value = False
mock_logger.confirm.return_value = False

with pytest.raises(SystemExit):
REAL_SETTINGS_STARTUP(Settings, bypass_login=False)

mock_open_pieces_widget.assert_not_called()
mock_logger.print.assert_called()
assert "enable auto-launch" in mock_logger.print.call_args_list[-1][0][0].lower()