Bug description
lean-ctx init --agent opencode and the OpenCode step of lean-ctx setup only look for ~/.config/opencode/opencode.json.
If the user's existing global OpenCode configuration is ~/.config/opencode/opencode.jsonc (a format explicitly supported by OpenCode), lean-ctx does not merge into that file. Instead it creates a new opencode.json containing only the lean-ctx MCP registration and shadow-mode permissions.
This makes the user's configured providers, models, agents, permissions, existing MCP servers, and other settings appear to have been wiped/replaced when OpenCode loads the newly created opencode.json.
The original opencode.jsonc remains on disk, but lean-ctx reports success against the newly created file:
✓ Shadow mode: native tools denied at ~/.config/opencode/opencode.json
✓ OpenCode MCP configured at ~/.config/opencode/opencode.json
Environment
- lean-ctx:
3.9.20 official Linux x86_64 release binary
- OS: Linux
- OpenCode global config:
~/.config/opencode/opencode.jsonc
Steps to reproduce
Use an isolated HOME:
export HOME="$(mktemp -d)"
mkdir -p "$HOME/.config/opencode"
cat > "$HOME/.config/opencode/opencode.jsonc" <<'JSONC'
{
// existing user configuration
"model": "openai/custom-model",
"provider": {
"openai": {
"options": { "baseURL": "https://example.invalid/v1" }
}
},
"mcp": {
"existing": {
"type": "local",
"command": ["existing-mcp"],
"enabled": true
}
}
}
JSONC
lean-ctx init --agent opencode
find "$HOME/.config/opencode" -maxdepth 1 -type f -print
cat "$HOME/.config/opencode/opencode.json"
The same writer path is reached by the OpenCode configuration step in non-interactive setup:
lean-ctx setup --non-interactive --yes --fix --skip-rules
Actual behavior
A second global config is created:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"lean-ctx": {
"command": ["/path/to/lean-ctx"],
"enabled": true,
"type": "local"
}
},
"permission": {
"bash": "deny",
"glob": "deny",
"grep": "deny",
"read": "deny"
}
}
The existing opencode.jsonc is not merged and does not receive the lean-ctx MCP entry.
Expected behavior
When configuring OpenCode, lean-ctx should:
- detect both
opencode.json and opencode.jsonc;
- if exactly one exists, merge the lean-ctx MCP entry into that existing file;
- preserve all unrelated configuration keys;
- avoid creating a competing second global config;
- define and document deterministic behavior if both files exist (preferably fail with a clear diagnostic unless OpenCode's precedence is unambiguous).
The same path-resolution logic should be shared by init, setup, doctor, shadow-mode permission updates, and uninstall.
Source-level root cause
At current main (5a90893092a7d31a8dae41ea6710b5a0c5048d15), rust/src/core/editor_registry/detect.rs hardcodes:
home.join(".config/opencode/opencode.json")
and the Windows branch likewise hardcodes opencode.json. The editor writer therefore never discovers an existing opencode.jsonc.
There is already JSONC-aware parsing in the writer, and other code recognizes both filenames (for example core/ide_permissions.rs checks opencode.json and opencode.jsonc), so config discovery is currently inconsistent.
Regression coverage suggested
Add integration tests for both init --agent opencode and setup with:
- only
opencode.json present;
- only
opencode.jsonc present;
- both present;
- JSONC comments/trailing syntax accepted by the existing parser;
- preservation of providers, models, agents, permissions, and foreign MCP entries.
wrap opencode note
On v3.9.20, lean-ctx wrap opencode does not modify the config; it exits with guidance to run lean-ctx init --agent opencode. Therefore the current reproducible paths are init and setup. Older versions may have routed wrap differently.
Related issues
This is a different path-discovery bug: an existing opencode.jsonc is ignored entirely and a competing opencode.json is created.
Bug description
lean-ctx init --agent opencodeand the OpenCode step oflean-ctx setuponly look for~/.config/opencode/opencode.json.If the user's existing global OpenCode configuration is
~/.config/opencode/opencode.jsonc(a format explicitly supported by OpenCode), lean-ctx does not merge into that file. Instead it creates a newopencode.jsoncontaining only the lean-ctx MCP registration and shadow-mode permissions.This makes the user's configured providers, models, agents, permissions, existing MCP servers, and other settings appear to have been wiped/replaced when OpenCode loads the newly created
opencode.json.The original
opencode.jsoncremains on disk, but lean-ctx reports success against the newly created file:Environment
3.9.20official Linux x86_64 release binary~/.config/opencode/opencode.jsoncSteps to reproduce
Use an isolated HOME:
The same writer path is reached by the OpenCode configuration step in non-interactive setup:
Actual behavior
A second global config is created:
{ "$schema": "https://opencode.ai/config.json", "mcp": { "lean-ctx": { "command": ["/path/to/lean-ctx"], "enabled": true, "type": "local" } }, "permission": { "bash": "deny", "glob": "deny", "grep": "deny", "read": "deny" } }The existing
opencode.jsoncis not merged and does not receive the lean-ctx MCP entry.Expected behavior
When configuring OpenCode, lean-ctx should:
opencode.jsonandopencode.jsonc;The same path-resolution logic should be shared by
init,setup, doctor, shadow-mode permission updates, and uninstall.Source-level root cause
At current
main(5a90893092a7d31a8dae41ea6710b5a0c5048d15),rust/src/core/editor_registry/detect.rshardcodes:and the Windows branch likewise hardcodes
opencode.json. The editor writer therefore never discovers an existingopencode.jsonc.There is already JSONC-aware parsing in the writer, and other code recognizes both filenames (for example
core/ide_permissions.rschecksopencode.jsonandopencode.jsonc), so config discovery is currently inconsistent.Regression coverage suggested
Add integration tests for both
init --agent opencodeand setup with:opencode.jsonpresent;opencode.jsoncpresent;wrap opencodenoteOn v3.9.20,
lean-ctx wrap opencodedoes not modify the config; it exits with guidance to runlean-ctx init --agent opencode. Therefore the current reproducible paths areinitandsetup. Older versions may have routedwrapdifferently.Related issues
opencode.jsoncould not be parsed.opencode.json.This is a different path-discovery bug: an existing
opencode.jsoncis ignored entirely and a competingopencode.jsonis created.