Handle existing OpenCode JSONC defaults (#1867)

Follow-up to #1866 addressing both Cubic findings before release:

- recognize schema-only OpenCode JSONC with comments and trailing commas
- exercise the intended preservation branch with valid JSON
- also verify administrator-selected JSONC remains byte-for-byte
unchanged

Verification: focused config tests pass (5 passed).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes detection of schema-only OpenCode JSONC configs so comments,
trailing commas, and escaped keys no longer prevent the default model
from being added.

- Replaces strict JSON parsing with a JSONC-aware parser that strips
comments and trailing commas before checking whether a config only sets
`$schema`.
- Writes the default config as JSONC text instead of JSON.
- Expands tests to cover plain JSON, JSONC with comments and trailing
commas, and escaped schema keys.

<sup>Written for commit c1358a2726298e20db1a8697647338baa21e555f.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/ArchiveBox/ArchiveBox/pull/1867?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Nick Sweeting 2026-09-03 11:22:33 -07:00 committed by GitHub
commit c28d470f9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 15 additions and 35 deletions

View File

@ -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:

View File

@ -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