From 27c2a947b425a5f85daf2c7ad77f97f54302afcc Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Thu, 3 Sep 2026 11:09:22 -0700 Subject: [PATCH] Handle existing OpenCode JSONC defaults --- archivebox/opencode/views.py | 19 +++++---------- archivebox/tests/test_opencode_agent.py | 31 +++++++------------------ 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/archivebox/opencode/views.py b/archivebox/opencode/views.py index f2913195..cc152887 100644 --- a/archivebox/opencode/views.py +++ b/archivebox/opencode/views.py @@ -2,7 +2,6 @@ from __future__ import annotations import atexit import base64 -import json import logging import os import re @@ -43,6 +42,11 @@ _PROXY_PREFIX_REGEX = _PROXY_PREFIX.replace("/", r"\/") _PROXY_PREFIX_NO_SLASH_REGEX = _PROXY_PREFIX.lstrip("/").replace("/", r"\/") _CONFIG_PATH = Path(opencode_plugin.__file__).with_name("config.json") _DEFAULT_MODEL = "opencode/big-pickle" +_DEFAULT_CONFIG = f'''{{ + "$schema": "https://opencode.ai/config.json", + "model": "{_DEFAULT_MODEL}" +}} +''' _TEXT_CONTENT_TYPES = ( "text/", @@ -323,19 +327,8 @@ def _ensure_project_files(settings: dict) -> None: opencode_skill_path.symlink_to(editable_skill_path) opencode_config_path = settings["config_home"] / "opencode" / "opencode.jsonc" - default_config = { - "$schema": "https://opencode.ai/config.json", - "model": _DEFAULT_MODEL, - } if not opencode_config_path.exists(): - opencode_config_path.write_text(f"{json.dumps(default_config, indent=2)}\n") - else: - try: - existing_config = json.loads(opencode_config_path.read_text()) - except (OSError, ValueError): - existing_config = None - if isinstance(existing_config, dict) and set(existing_config) <= {"$schema"}: - opencode_config_path.write_text(f"{json.dumps(default_config, indent=2)}\n") + opencode_config_path.write_text(_DEFAULT_CONFIG) def _ensure_default_session(settings: dict) -> str: diff --git a/archivebox/tests/test_opencode_agent.py b/archivebox/tests/test_opencode_agent.py index ff639958..9cc9aad9 100644 --- a/archivebox/tests/test_opencode_agent.py +++ b/archivebox/tests/test_opencode_agent.py @@ -445,13 +445,20 @@ def test_opencode_state_dir_is_separate_from_workdir(tmp_path): assert json.loads((state_dir / "config" / "opencode" / "opencode.jsonc").read_text())["model"] == "opencode/big-pickle" -def test_opencode_preserves_existing_config(tmp_path): +@pytest.mark.parametrize( + "existing_config", + [ + '{"model": "anthropic/claude-sonnet-4-5"}\n', + '{\n // Keep the administrator-selected model.\n "model": "anthropic/claude-sonnet-4-5",\n}\n', + '{\n // Schema-only files are still user-owned.\n "$schema": "https://opencode.ai/config.json",\n}\n', + ], +) +def test_opencode_preserves_existing_config(tmp_path, existing_config): from archivebox.opencode import views state_dir = tmp_path / "state" config_path = state_dir / "config" / "opencode" / "opencode.jsonc" config_path.parent.mkdir(parents=True) - existing_config = '{\n // Keep the administrator-selected model.\n "model": "anthropic/claude-sonnet-4-5"\n}\n' config_path.write_text(existing_config) views._ensure_project_files( @@ -466,26 +473,6 @@ def test_opencode_preserves_existing_config(tmp_path): assert config_path.read_text() == existing_config -def test_opencode_adds_default_model_to_schema_only_config(tmp_path): - from archivebox.opencode import views - - state_dir = tmp_path / "state" - config_path = state_dir / "config" / "opencode" / "opencode.jsonc" - config_path.parent.mkdir(parents=True) - config_path.write_text('{"$schema": "https://opencode.ai/config.json"}\n') - - views._ensure_project_files( - views._settings( - { - "OPENCODE_WORKDIR": str(tmp_path / "workdir"), - "OPENCODE_STATE_DIR": str(state_dir), - }, - ), - ) - - assert json.loads(config_path.read_text())["model"] == "opencode/big-pickle" - - def test_opencode_defaults_to_the_archivebox_collection(): from archivebox.opencode import views