feat(opencode): support jsonc configs with comment preservation#41
Merged
feat(opencode): support jsonc configs with comment preservation#41
Conversation
OpenCode's loader runs every config file through jsonc-parser regardless of extension (packages/opencode/src/config/config.ts uses ConfigParse.jsonc for both opencode.json and opencode.jsonc), so a single `//` comment or trailing comma in the user's opencode.json silently emptied HK's MCP list under serde_json::from_str. Match upstream's permissive parsing in `OpencodeAdapter::parse_json`. Path resolution also gains a `.jsonc` preference: `mcp_config_path` and the new `mcp_config_path_for` override return an existing `.jsonc` over `.json` (matching OpenCode's load-order precedence β `.jsonc` wins on conflict). Falls back to `.json` when neither exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch project-scope MCP discovery from a manual `project_mcp_config_relpath().join()` to the trait's scope-aware `mcp_config_path_for(&scope)` accessor. The default trait impl is behaviorally equivalent for adapters that use a single canonical filename (Claude `.mcp.json`, Cursor `.cursor/mcp.json`, etc.), so this is a no-op for them. It gives OpenCode the override hook needed to prefer an existing opencode.jsonc over opencode.json β without this change, `is_file()` on the hardcoded `.json` path would silently miss jsonc-only projects. Also collapses the now-redundant nested `if` to satisfy clippy's collapsible_if lint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Disable / Enable / Delete / cross-agent install of an OpenCode MCP entry now flow through jsonc-parser's CST API instead of serde_json::Value round-trip. The CST holds comments, whitespace, and trailing commas as first-class nodes, so `to_string()` after a targeted mutation regenerates the file with everything outside the touched entry kept verbatim. Architecture: each McpFormat::Opencode dispatch routes to a dedicated function (`deploy_mcp_server_opencode`, `remove_mcp_server_opencode`, `restore_mcp_server_opencode`, `read_mcp_server_config_opencode`) sharing a new `locked_modify_jsonc` helper that mirrors `locked_modify_json`'s advisory-lock-and-rewrite semantics. Other JSON-format agents (Claude, Cursor, Antigravity, Copilot, Gemini) keep the strict serde_json path unchanged β accidentally accepting jsonc syntax in their configs would surface entries the agent itself can't load. By design, a comment line directly above a removed entry is left in place β HK never edits user comment text, only its own data entries. The orphan comment is the user's content to keep or clean up. `json_top_key`'s Opencode arm is hardened to `unreachable!()` (matching Toml's pattern) so any future regression that re-routes Opencode through the generic JSON path fails loudly instead of silently producing strict JSON output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
opencode.json[c]had a//comment or trailing comma βserde_json::from_strrejects what OpenCode itself happily parses (its loader runs both extensions through jsonc-parser). NowOpencodeAdapter::parse_jsonmatches upstream, andmcp_config_path[_for]smart-picks an existing.jsoncover.json(matches OpenCode's load-order precedence).McpFormat::Opencodedispatch routes to a dedicated function backed by a newlocked_modify_jsonchelper that round-trips the file through jsonc-parser's CST API; everything outside the touched entry stays verbatim..mcp.json, Cursor, Antigravity, Copilot, Gemini) keep their strictserde_jsonpath β accidentally accepting jsonc syntax in their configs would surface entries the agent itself can't load.Design notes
McpFormat::Opencodegoes through*_opencodefunctions, regardless of.jsonvs.jsoncextension.json_top_key's Opencode arm is hardened tounreachable!()so any future regression that re-routes through the generic JSON path fails loudly.mcp_config_path_for(&scope)instead of joiningproject_mcp_config_relpath()directly. Default trait impl is no-op for adapters with a single canonical filename β only OpenCode overrides to probe disk state, so jsonc-only projects are now discoverable.jsonc-parser0.32 withserde + serde_json + cstfeatures. Same library OpenCode uses (TS twin); also Deno's pinned dep. Active, 1.5M dl/90d.Test plan
cargo test -p hk-core --libβ 376 passed (12 new)cargo clippy -p hk-core --lib --tests -- -D warningscleancargo build --workspaceβ hk-core / hk-web / hk-desktop / hk-cli all pass/private/tmp/hk-opencode-test/opencode.jsonc(with//comments + trailing comma):projectScopeBlockedgate); covered by unit testtest_deploy_mcp_server_opencode_preserves_commentsand shares CST code path with restore/enableOut of scope (explicit)
.jsonand.jsonc(HK picks.jsoncwhen both present, matching OpenCode's load-order winner; merging both into a single read is roadmap'd as a P2 follow-up).π€ Generated with Claude Code