Skip to content

Install OpenCode hooks in the terminal's config directory - #25296

Merged
nwparker merged 8 commits into
mainfrom
orca/opencode-consumer-config-root
Oct 4, 2026
Merged

nwparker merged 8 commits into
mainfrom
orca/opencode-consumer-config-root

Conversation

@nwparker

@nwparker nwparker commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​199 $\color{#cf222e}{\Huge{\mathbf{−}}}$​17 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​182
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​33 $\color{#cf222e}{\Huge{\mathbf{−}}}$​6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​27

ELI5

When a terminal uses a different home or config directory from Orca, OpenCode can miss Orca's plugin. Install the plugin where that terminal's OpenCode actually reads its configuration.

What Changed

Pass the execution environment's config directory to the existing installer. Read supported literal XDG_CONFIG_HOME exports from the selected shell's profile. Explicit directories retain their existing overlay path.

Use HOME on macOS/Linux and USERPROFILE on Windows. SSH uses the execution host's environment and shell; WSL delegates installation to the guest. Folder workspaces use the same path.

Why

Installation previously used Orca's GUI environment. Reusing the resolver, installer, and static shell reader fixes that mismatch without starting a shell or redirecting the user's settings JSON.

Linked Issue

Continues #9192 and credits @inaemin for the original contribution. This addresses plugin placement; the original PR remains open for its broader OMO and per-agent configuration work.

Visual Proof

N/A — this changes plugin installation. The before/after evidence comes from the installed OpenCode config and plugin APIs.

Testing

  • Installed OpenCode 2.0.16 on macOS: aligned paths, different home, different XDG directory, literal shell export, and explicit directory.
  • Added tests for homes, config precedence, selected shells, overlays, disabled hooks, WSL, and relay installation.

Active Orca plugin counts changed from [1, 0, 0, 0, 1] to [1, 1, 1, 1, 1], with exact plugin ID, local path, and active state required. All 15 read-only responses passed; all 15 child processes exited normally and five private endpoints closed.

External settings JSON already loaded before the fix. Build.model remained absent, so effective per-agent OMO selection is unproved. The shell-export case used the static reader.

162 tests passed across nine files. All three typechecks passed on integration 3a9fd8c0; changed-file lint, format, six gates, and official anti-slop passed on the qualified repair. Windows/WSL/SSH coverage is from source tests. Full build remains for CI.

The test-only follow-up 153131cd preserves those eleven files and updates five existing PTY test expectations to verify the terminal config directory. Both affected suites pass all 40 tests; scoped lint and formatting pass.

Review

Independently reviewed source and runtime evidence, including execution-host ownership, explicit config precedence, and selected-shell propagation.

Agent skill upstream boundary

  • Not applicable; no upstream skill-installer material is copied or translated.

Notes

The private background test harness invokes the existing installer, not Orca's GUI or a real terminal. No provider turns or user settings writes.

Checklist

  • This PR is small and focused
  • Explained the user-facing before/after, mechanism, and choice of approach
  • Visual proof is N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH, folder workspace, and path impact considered
  • Focused tests, typechecks, changed-file lint, format, and quality checks passed; full build remains for CI

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Execution-home config resolution — resolveOpenCodeConfigDirectory now derives the install root from HOME (POSIX) / USERPROFILE (Windows), falling back to homedir(), while XDG_CONFIG_HOME still takes precedence.
  • Shell-startup XDG_CONFIG_HOME — the main host-env path and the relay both read a literal XDG_CONFIG_HOME export from the selected shell's startup files and use it as the install root.
  • shellPath plumbing — BuildPtyHostEnvOptions.shellPath is threaded through the local, Codex, and daemon spawn paths so the startup read follows the selected shell rather than inherited SHELL.
  • Tests — resolver unit tests plus main-path and relay-path tests for home / XDG / selected-shell roots, along with a disabled-hooks guard.

The default (no custom home) behavior is unchanged: homedir() already resolves to HOME/USERPROFILE in the normal case, and the install still lands in XDG_CONFIG_HOME when set. WSL correctly skips the host install, Windows reads nothing from POSIX startup files, and the Windows path shape (USERPROFILE/.config/opencode) matches existing repo knowledge in src/main/skills/skill-discovery-sources.ts. Focused suites (85 + 133 tests across the changed and adjacent files) pass.

ℹ️ App-start refresh does not cover alternate config roots

Because installs can now land in a non-default execution home, the startup refresh that pushes an updated plugin to an already-running OpenCode 2 service no longer sees those installs — they are only refreshed on the next pane spawn in that terminal.

Technical details
# Alternate-home installs are outside the refresh scan

## Affected sites
- `src/main/opencode/hook-service.ts:204` — `refreshInstalledPlugins` scans only `resolveOpenCodeConfigDirectory()` (the process/default home) plus the userData overlays.
- `src/relay/relay-agent-hook-runtime.ts:222` — the relay install-plugins handler refreshes only `process.env`/`homedir()` with `onlyIfInstalled: true`.
- `src/main/ipc/pty/host-env/opencode-config.ts:76` — the new install root is derived per pane from the execution env, so it can differ from the default home.

## Required outcome
None required for merge. The gap is bounded: `buildPtyEnv` rewrites the current plugin into the execution root on the next spawn, so a stale install self-heals. The only concrete fallout is a running OpenCode 2 service in an alternate-home config dir that keeps the old plugin until a pane in that terminal launches (or the app is restarted in a way that re-touches that root).

## Open questions for the human
- Is the delayed refresh acceptable, or should the app-start path track the alternate roots Orca has written into (for example persisting them alongside the overlay manifests) so `refreshInstalledPlugins` can reach them?

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

OpenCode plugin installation now selects its config root from execution environment values, including platform-specific home values and XDG_CONFIG_HOME. Local PTY launches also use XDG_CONFIG_HOME from the selected shell’s startup environment when available. Relay launches pass the shell startup value to plugin installation. Tests cover config root selection, config preservation, environment values, overlay destinations, and disabled hooks.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 15313

Users whose shell conditionally sets XDG_CONFIG_HOME may start OpenCode without the installed plugin. This is a bounded configuration case; correct the shared reader before relying on it in both launch paths.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 15313

Plugin placement now follows the terminal’s configuration while preserving explicit-directory handling and guest-owned installation. Risk is low, with remaining uncertainty around complex shell profiles and maintaining plugins in previously selected locations.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Controlling terminal environment values or a scanned profile assignment can redirect plugin installation. Effective write exposure includes the selected root and canonical symlink targets accessible to the installer’s filesystem identity; the config root is not a confinement boundary. No privilege elevation was demonstrated by the changed resolution logic.

Trust Boundaries and Controls

  • observed — Profile discovery reads files rather than executing a shell. Local installation remains gated by enabled-hook agent selection; relay installation requires a cached plugin source. The inspected status plugin obtains credentials at runtime from the endpoint file or environment and requires a port, token and pane identity before posting to loopback with a token header. These are client-side controls, not proof of server-side authorization.

Resilience and Maintainability Implications

  • inferred — Automatic local refresh enumerates the current ambient canonical root and managed overlays; relay connection refresh likewise uses process.env. Alternate terminal-selected roots are repaired on eligible launches but are not necessarily reached by those refresh sweeps. clearPty deliberately does not delete canonical installations. Historical-root maintenance and disablement remain lifecycle uncertainties, without an established vulnerability in retained plugin content.

Hardening Proposals

  • proposed — Define the contract for best-effort shell-root discovery and historical plugin maintenance. An ownership-aware record of installed roots could support refresh and recovery without recursively deleting user configuration or treating disabled future installation as file revocation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 13 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: installing OpenCode hooks in the terminal’s config directory.
Description check ✅ Passed The description covers the required sections with a clear explanation of the change, motivation, linked issue reference, visual-proof rationale, testing, and checklist. It also documents known testing…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Ignore conditional XDG assignments when choosing the plugin root. · opencode-config.ts:75-80

src/main/ipc/pty/host-env/opencode-config.ts:75-80
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Ignore conditional XDG assignments when choosing the plugin root.

When a multiline conditional in the selected shell’s startup file is false, its standalone export XDG_CONFIG_HOME=... can still match the reader. If that literal differs from the inherited XDG value, the default plugin-install branch can write under <literal>/opencode/plugins, while OpenCode resolves a different config root and can miss the plugin. Make the shared reader return only values it can establish as unconditional; the relay callsite uses the same reader.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d2ddc04e-e792-4764-9bb6-cd343aca112f
📥 Commits

Reviewing files that changed from the base of the PR and between 3a9fd8c and 153131c.

📒 Files selected for processing (2)
  • src/main/ipc/pty-daemon-spawn-codex-auth.test.ts
  • src/main/ipc/pty-spawn-env-agent-overlays.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Following the prior pullfrog review at 3a9fd8c0, the only new commit (153131cd) is test-only:

  • PTY fixture assertions for the consumer config root — pty-daemon-spawn-codex-auth.test.ts and pty-spawn-env-agent-overlays.test.ts now set an explicit XDG_CONFIG_HOME in the pane env and assert buildPtyEnv is called with the derived <XDG_CONFIG_HOME>/opencode root as its third argument, covering the mirror and no-source-dir cases plus the issue #1534 zsh startup-export case.

The added assertions depend on the new three-argument buildPtyEnv signature, so they fail against the prior two-argument call — real coverage, not theatre. No production lines changed in the delta; the prior review's findings stand unchanged.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@nwparker
nwparker merged commit 8ff6ec9 into main Oct 4, 2026
59 checks passed
diegoesolorzano added a commit to diegoesolorzano/orca that referenced this pull request Oct 11, 2026
Integrate ~2007 upstream commits, mostly OpenCode v2 integration maturity
(status reporting per pane stablyai#23722/stablyai#24962, hook install in terminal config
dir stablyai#25296, stop stale plugin from retired shared hooks dir stablyai#23500) plus
@orca/process-host extraction, structured chat over ACP, and many agents.

Fork conflicts resolved (6): minimax/zai catalog entries relocated to the
new split agent-catalog-entries-secondary.ts; redraw shortcut moved to the
new matches() helper; createLocalNestedRepoScanFilesystem updated to
isGitRepoAsync; nestedReposPromise kept after upstream made timing a param;
showNestedReposToast import kept, dead settingsForRepoOwner dropped.
Added minimax/zai to the new Record<TuiAgent> maps (rest-signal, readiness
lane) and switched the Activity wrapper-grouping types to TerminalAgent.
Accepted upstream's deletion of the global-fetch audit test (stablyai#25791).

typecheck clean; git-crypt, time-tracker, activity and shortcut-policy
suites green (2305 tests).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants