From 41deb48005b25b43efd8524b8350b45f5333673c Mon Sep 17 00:00:00 2001 From: somew1nd Date: Tue, 1 Sep 2026 16:08:47 -0700 Subject: [PATCH] fix(cli): preserve invalid config when backup fails --- artemis/interfaces/cli/commands/mcp.py | 4 +++- tests/unit/test_cli.py | 28 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/artemis/interfaces/cli/commands/mcp.py b/artemis/interfaces/cli/commands/mcp.py index 6ec5d10..7e33a94 100644 --- a/artemis/interfaces/cli/commands/mcp.py +++ b/artemis/interfaces/cli/commands/mcp.py @@ -219,8 +219,10 @@ def _merge_json_file( except OSError as exc: logger.warning( f"Existing JSON in {file_path} could not be parsed AND the backup to" - f" {backup_path} failed ({exc}); the original content will be overwritten." + f" {backup_path} failed ({exc}); refusing to rewrite it." + " Fix or remove the file, then re-run the install." ) + return False for remove_path in remove_paths: parent = data diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index 6e9be2d..1e0091e 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -374,6 +374,34 @@ def test_cli_mcp_install_jsonc_and_backup(tmp_path, monkeypatch): assert "INVALID JSON DATA" in backup_file.read_text(encoding="utf-8") +def test_cli_mcp_install_refuses_rewrite_when_backup_fails(tmp_path, monkeypatch): + """Backup failure for an unparseable config must abort the rewrite instead of destroying it.""" + monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) + monkeypatch.setattr("mcp_server.utils.env_utils.get_project_root", lambda: str(tmp_path)) + + cursor_dir = tmp_path / ".cursor" + cursor_dir.mkdir(parents=True, exist_ok=True) + mcp_json = cursor_dir / "mcp.json" + original = "INVALID JSON DATA {{{{" + mcp_json.write_text(original, encoding="utf-8") + + import pathlib + + real_write_text = pathlib.Path.write_text + + def failing_bak_write(self, *args, **kwargs): + if str(self).endswith(".bak"): + raise OSError("disk full") + return real_write_text(self, *args, **kwargs) + + monkeypatch.setattr("pathlib.Path.write_text", failing_bak_write) + + runner.invoke(app, ["mcp", "--install", "cursor"]) + + assert mcp_json.read_text(encoding="utf-8") == original + assert not (cursor_dir / "mcp.json.bak").exists() + + def test_cli_mcp_install_openclaw_migrates_legacy_plugin_shape(tmp_path, monkeypatch): """Verify reinstalling OpenClaw replaces the obsolete plugin wrapper with mcp.servers.""" monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)