Skip to content

bug: OpenCode setup ignores existing opencode.jsonc and creates MCP-only opencode.json #1585

Description

@Manaszz

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:

  1. detect both opencode.json and opencode.jsonc;
  2. if exactly one exists, merge the lean-ctx MCP entry into that existing file;
  3. preserve all unrelated configuration keys;
  4. avoid creating a competing second global config;
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAgent integrations (pi, copilot, cursor, claude, …)bugSomething isn't workingconfigConfiguration (config.toml, persistence, schema)priority: highImportant — schedule soonstatus: integratedFix merged to main but NOT yet in a tagged release

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions