Skip to content

OpenCode V2: install skips an existing opencode.json, so no agents and no skills path are ever wired #3559

Description

@Ducquangbill

Summary

On a machine that already has ~/.config/opencode/opencode.json, an OpenCode install copies the whole catalog but never writes ECC's config, so the pieces that depend on config are silently missing. Two separate consequences:

  1. No agents are installed. The opencode target declares its 26 subagents inside .opencode/opencode.json under the agent map (each with prompt: "{file:prompts/agents/<name>.txt}"). It does not ship agents/*.md files. Because that config file is skipped, ~/.config/opencode/ ends up with no planner, tdd-guide, code-reviewer, security-reviewer, ... — while commands/*.md reference exactly those agents in frontmatter (commands/plan.md → agent: planner, commands/tdd.md → agent: tdd-guide). 14 of the installed commands delegate to an agent that does not exist.

  2. skills.paths is never applied. The template sets "skills": { "paths": ["../skills"] }, which is CWD-relative. It resolves against the user's project directory (e.g. D:/School/skills), not the installed config root. (Same finding as the note in PR fix: port OpenCode plugin from v1 to v2 plugin API #3536; reported here because it is a consequence of the skip for existing-config users.)

Separately: on OpenCode V2 the top-level instructions field is accepted but its entries are not loaded (V2 docs: "OpenCode accepts this field but does not load its entries; use AGENTS.md for instructions"). ECC relies on instructions for its curated instruction set, so none of it takes effect on V2 regardless of whether the config file is written.

Why the config is never written

scripts/lib/install/ownership-guard.js:77-89: a copy-file operation is skipped with a warning when the destination already exists and is not recorded as ECC-managed in the previous install-state — correct behavior for user-owned files, but it also means the ECC config template is never applied to anyone who already has an opencode.json:

Skipped user-owned file ~/.config/opencode/opencode.json: the existing file is not recorded in ECC install-state.

So the install reports success, writes ~1100 files, and leaves the active config completely unwired.

Repro

Windows 11, native. OpenCode v2.0.26. ECC 2.2.3.

git clone https://github.com/affaan-m/ECC
cd ECC
npm install
npm run build:opencode
node scripts/install-apply.js --target opencode --profile full --no-hooks

(with an existing ~/.config/opencode/opencode.json present)

Observed

  • Install completes: Operations: 835 planned, Skipped operations: 3 (package.json, package-lock.json, opencode.json).
  • ~/.config/opencode/agents/ contains no ECC agents.
  • commands/plan.md frontmatter: agent: planner — that agent does not exist.
  • ~/.config/opencode/opencode.json has no ECC keys (no plugin/plugins, no agent/agents, no skills, no command/commands).
  • ~/.config/opencode/instructions/INSTRUCTIONS.md is written to disk but nothing loads it on V2.

Impact

Users with an existing OpenCode config get a partial install that looks complete: commands appear (they are discovered from the commands/ directory) but every command that delegates to an ECC agent cannot resolve it, and the curated instructions never load. This is close to "installed but does nothing useful", which is likely a strong uninstall trigger.

Suggested fixes

  • Do not skip the config outright. Merge only ECC-owned keys into an existing opencode.json (preserving user keys) and record them as managed, so repair/uninstall can reverse them. deepMergeJson already exists in scripts/lib/install/json-ops.js.
  • Ship the 26 opencode agents as file-based definitions (agents/<name>.md with description + mode: subagent) instead of agent entries in config. Then agents do not depend on the config file at all, and they are discovered by directory.
  • Replace the instructions array with an AGENTS.md for V2 (global ~/.config/opencode/AGENTS.md), since V2 does not load instructions.
  • Make skills.paths absolute to the installed config root, or drop it and rely on V2's automatic discovery of the config directory's skills/.

Relations

Environment

  • ECC 2.2.3
  • OpenCode v2.0.26
  • Windows 11, native (no WSL)

Activity

  1. Ducquangbill commented on Oct 13, 2026

    @Ducquangbill
    Author

    Additional finding: uninstall writes a Claude-harness state store on an OpenCode-only machine

    Related residue from the same target, found while verifying the uninstall path.

    On a machine that has never used Claude Code (no ~/.claude at all), running the OpenCode uninstall creates one:

    node scripts/uninstall.js --target opencode

    Result:

    ~/.claude/ecc/state.db    131072 bytes
    

    ~/.claude did not exist before the command, and its only content afterwards was ecc/state.db.

    Cause:

    • scripts/lib/state-store/index.js:22 hardcodes the default store location:
      const DEFAULT_STATE_STORE_RELATIVE_PATH = path.join('.claude', 'ecc', 'state.db');
    • resolveStateStorePath() (index.js:203) joins it onto homeDir, with no target awareness.
    • scripts/uninstall.js:183-191 calls reconcileCanonicalInstallStates() for every target, which opens the store and creates the file.

    So the derived projection is pinned to the Claude harness directory regardless of --target.

    Two consequences:

    1. Cross-harness pollution. An OpenCode-only (or Codex-only, Cursor-only, …) user gets a ~/.claude tree created by ECC.
    2. The file is not ECC-managed. It is not recorded in ecc-install-state.json, so uninstall does not remove it, and doctor --target opencode still reports Status: OK. The residue survives a full uninstall and is only found by scanning the filesystem.

    Suggested fix: resolve the projection path from the target's own config root (the opencode target already has scripts/lib/opencode-paths.js:resolveOpencodeConfigRoot), or keep the store under the install root / a target-scoped directory, and have uninstall remove it.

    Verification context

    Windows 11 native, OpenCode v2.0.26, ECC 2.2.3. After removing ~/.claude manually and re-scanning the whole home directory for name and content matches (ecc-install-state.json, .pi/, ecc-universal, affaan-m/ECC), the only remaining traces were unrelated shell history lines.

  2. pradeep14012004 commented on Oct 14, 2026

    @pradeep14012004

    Hi there! I'd love to help out with this one! The installation logic does seem worthy of looking into, particularly the effect on people who are already using OpenCode. Do you think you could allow an external PR for this issue? I would love to help make a focused fix as per your repository's contribution guidelines.
    Thanks

  3. akushonkamen commented on Oct 15, 2026

    @akushonkamen
    Contributor

    Confirmed the root cause by reading the code at HEAD: scripts/lib/install/ownership-guard.js:77-89 skips any copy-file whose destination already exists and isn't recorded in the previous install-state — which also hits ECC's own opencode.json template planned as a managed operation by scripts/lib/install-targets/opencode-home.js:106-112. So existing-config users never get the agent map, skills.paths, or plugin wiring. deepMergeJson (scripts/lib/install/json-ops.js:85) is already available for the fix.

    I'm working on a focused fix: merge only ECC-owned keys into an existing opencode.json (preserving user keys), record the merged keys in install-state so repair/uninstall stays reversible. Filed on behalf of an AI-assisted workflow (Claude by Anthropic); I'll author and review the changes. Will follow up with a PR shortly.

  4. akushonkamen commented on Oct 15, 2026

    @akushonkamen
    Contributor

    Opened the fix as #3599: the managed config template now merges into an existing opencode.json instead of being skipped (user keys preserved; pre-merge content snapshotted in install-state so uninstall restores exact bytes; unrecorded hook-consent still refuses the install; repair merges the same way). Red/green reproduction, focused suites and the full node tests/run-all.js run (7800 passed / 0 failed, 423 files) are documented in the PR body. Root-cause analysis and AI disclosure are in my claim comment above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions