Skip to content

fix(server): report OpenCode skills in the provider snapshot - #5683

Closed
vraj-ai wants to merge 3 commits into
pingdotgg:mainfrom
vraj-ai:fix/opencode-provider-skills
Closed

fix(server): report OpenCode skills in the provider snapshot#5683
vraj-ai wants to merge 3 commits into
pingdotgg:mainfrom
vraj-ai:fix/opencode-provider-skills

Conversation

@vraj-ai

@vraj-ai vraj-ai commented Aug 8, 2026

Copy link
Copy Markdown

Problem

The OpenCode provider snapshot never populates skills, so the $ picker is always empty for OpenCode models. Claude and Codex both list theirs — ClaudeProvider calls discoverClaudeSkills, CodexProvider parses skills/list — but OpenCodeProvider has no skill code at all, so ServerProvider.skills falls back to its [] default.

Users with skills installed in OpenCode's own directories see them work through OpenCode's built-in skill tool, but they never appear in T3 Code's picker, which makes it look like the skills aren't loaded.

Approach

Filesystem discovery, mirroring the existing ClaudeSkills driver.

I looked at reading the SDK instead (client.skill.list()), but the local install path builds its inventory from opencode models / opencode agent list and never starts a server — and there is no opencode skill CLI command. Using the SDK would mean spawning a server purely to probe, and would leave the common non-serverUrl install with no skills. Scanning the filesystem covers both install paths with no extra process.

OpenCodeSkills scans OpenCode's skill roots:

  • user: <config dir>/skill and <config dir>/skills
  • project: <cwd>/.opencode/skill and <cwd>/.opencode/skills

honouring OPENCODE_CONFIG_DIR then XDG_CONFIG_HOME then ~/.config, matching what OpenCode itself resolves, so the picker reflects the directories the spawned runtime actually reads. It matches OpenCode's {skill,skills}/**/SKILL.md, so nested skills are found via a depth-bounded walk, and stat follows symlinks because the common multi-agent layout symlinks each skill into the config dir.

Discovery is best-effort in the same way as ClaudeSkills: unreadable roots and entries without parseable frontmatter are skipped, so a broken skill can never degrade the snapshot. Project scope wins name collisions, matching OpenCode's later-scan-wins resolution.

Scope note

Two lines of the diff widen checkOpenCodeProviderStatus's context to FileSystem | Path and provide those services in OpenCodeDriver, exactly as ClaudeDriver already does for checkClaudeProviderStatus. Both were already in OpenCodeDriver's declared requirements.

This does not touch skills.paths or skills.urls from opencode.json. The server is spawned with OPENCODE_CONFIG_CONTENT={}, so those never apply to skills in T3 Code today, and covering them would mean parsing a config that is deliberately discarded.

Verification

  • pnpm test src/provider — 491 passed, 0 failures
  • pnpm typecheck — clean
  • vp lint / vp format --check — clean
  • 4 new tests: user + project discovery, singular skill dir and nested skills, project-wins collision, and skipping malformed/unnamed frontmatter and missing roots
  • Against a real install (37 skills in ~/.config/opencode/skills), discovery returns all of them with correct paths and descriptions; before this change the snapshot reported none

On CONTRIBUTING

I read it, and I realise this is closer to "feature work" than a pure bug fix, so close it if it is not wanted. I kept it to ~19 lines of wiring plus one new driver and its tests, and modelled it as closely as I could on ClaudeSkills rather than introducing a new pattern.

🤖 Generated with Claude Code


Note

Low Risk
Read-only filesystem probing during provider status checks; failures are ignored so a broken skill tree cannot break the snapshot.

Overview
OpenCode provider snapshots now include skills so the $ picker can list them like Claude and Codex, instead of always returning an empty skills array.

Adds discoverOpenCodeSkills, which scans user config (skill / skills under the resolved OpenCode config dir) and project workspace (.opencode/skill(s)), finds nested SKILL.md files, parses YAML frontmatter for name / description, skips bad entries, and lets project scope override user on name clashes. checkOpenCodeProviderStatus calls that helper on successful probes and passes the result into buildServerProvider. OpenCodeDriver wires FileSystem and Path into the status-check effect (same pattern as Claude). New unit tests cover discovery, nesting, collisions, and malformed files.

Reviewed by Cursor Bugbot for commit fd5a0c3. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Report OpenCode skills in the provider snapshot by discovering skill files from the filesystem

  • Adds discoverOpenCodeSkills to walk user (~/.config/opencode/{skill,skills}) and project (.opencode/{skill,skills}) directories, parsing YAML frontmatter from SKILL.md files to extract name and description.
  • Project-scoped skills override user-scoped ones on name collision; results require a valid name field and are sorted alphabetically.
  • Updates checkOpenCodeProviderStatus to call discoverOpenCodeSkills and include the resulting skills array in the returned ServerProviderDraft.
  • Provides FileSystem and Path services to the checkOpenCodeProviderStatus effect in OpenCodeDriver.create to satisfy the updated effect environment.
📊 Macroscope summarized fd5a0c3. 3 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

The OpenCode provider snapshot never populated `skills`, so the `$` picker
was always empty for OpenCode models while Claude and Codex both listed
theirs. Neither `opencode models` nor `opencode agent list` reports skills,
and the SDK only exposes them once a server is running, so discovery scans
the filesystem instead — the same approach `ClaudeSkills` already takes for
Claude Code, and it keeps the probe free of an extra process spawn.

Scans OpenCode's own skill roots (`<config dir>/skill(s)` and
`<cwd>/.opencode/skill(s)`), honouring `OPENCODE_CONFIG_DIR` and
`XDG_CONFIG_HOME` so it reads the directories the spawned runtime would.
Discovery is best-effort: unreadable roots and skills without parseable
frontmatter are skipped so a broken skill can never degrade the snapshot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 59cbd732-c40f-410e-884c-fe21eb7ac97a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 8, 2026
DEFAULT_OPENCODE_MODEL_CAPABILITIES,
);
const connectedCount = inventoryExit.value.providerList.connected.length;
const skills = yield* discoverOpenCodeSkills(cwd, resolvedEnvironment);

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.

🟠 High Layers/OpenCodeProvider.ts:440

checkOpenCodeProviderStatus always calls discoverOpenCodeSkills(cwd, resolvedEnvironment) locally, even when isExternalServer is true. The resulting skills are published in the provider snapshot for a remote OpenCode server running on another machine with a different config directory and workspace, so the snapshot advertises skills the remote server cannot load and omits skills installed on the remote server. Selecting those skills from the $ picker is incorrect. Consider using the remote SDK skill inventory in the external-server branch, or gating the local discovery call to the non-external path.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/OpenCodeProvider.ts around line 440:

`checkOpenCodeProviderStatus` always calls `discoverOpenCodeSkills(cwd, resolvedEnvironment)` locally, even when `isExternalServer` is true. The resulting skills are published in the provider snapshot for a remote OpenCode server running on another machine with a different config directory and workspace, so the snapshot advertises skills the remote server cannot load and omits skills installed on the remote server. Selecting those skills from the `$` picker is incorrect. Consider using the remote SDK skill inventory in the external-server branch, or gating the local discovery call to the non-external path.

Comment on lines +81 to +84
const xdgConfigHome = environment.XDG_CONFIG_HOME?.trim() ?? "";
const configHome =
xdgConfigHome.length > 0 ? path.resolve(xdgConfigHome) : path.join(NodeOS.homedir(), ".config");
return path.join(configHome, "opencode");

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.

🟡 Medium Drivers/OpenCodeSkills.ts:81

When OPENCODE_CONFIG_DIR is unset but XDG_CONFIG_HOME is also unset, resolveOpenCodeConfigDirPath falls back to NodeOS.homedir() — the parent process's home. A provider instance that overrides HOME (or USERPROFILE) in its environment without setting XDG_CONFIG_HOME will spawn OpenCode with that overridden home, but discovery scans the parent process's ~/.config/opencode instead. This silently omits the instance's configured skills and, in multi-tenant deployments, can surface skills belonging to a different user. The home directory should be resolved from environment.HOME / environment.USERPROFILE before falling back to NodeOS.homedir().

Suggested change
const xdgConfigHome = environment.XDG_CONFIG_HOME?.trim() ?? "";
const configHome =
xdgConfigHome.length > 0 ? path.resolve(xdgConfigHome) : path.join(NodeOS.homedir(), ".config");
return path.join(configHome, "opencode");
const xdgConfigHome = environment.XDG_CONFIG_HOME?.trim() ?? "";
const configHome =
xdgConfigHome.length > 0
? path.resolve(xdgConfigHome)
: environment.HOME?.trim() || environment.USERPROFILE?.trim() || ""
? path.resolve(environment.HOME?.trim() || environment.USERPROFILE?.trim() || "")
: path.join(NodeOS.homedir(), ".config");
return path.join(configHome, "opencode");
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/OpenCodeSkills.ts around lines 81-84:

When `OPENCODE_CONFIG_DIR` is unset but `XDG_CONFIG_HOME` is also unset, `resolveOpenCodeConfigDirPath` falls back to `NodeOS.homedir()` — the parent process's home. A provider instance that overrides `HOME` (or `USERPROFILE`) in its environment without setting `XDG_CONFIG_HOME` will spawn OpenCode with that overridden home, but discovery scans the *parent* process's `~/.config/opencode` instead. This silently omits the instance's configured skills and, in multi-tenant deployments, can surface skills belonging to a different user. The home directory should be resolved from `environment.HOME` / `environment.USERPROFILE` before falling back to `NodeOS.homedir()`.

/** OpenCode accepts both spellings of the skills directory. */
const SKILL_DIRECTORY_NAMES = ["skill", "skills"] as const;

/**

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.

🟡 Medium Drivers/OpenCodeSkills.ts:29

MAX_SKILL_DEPTH = 5 causes collectSkillFiles to silently drop any SKILL.md nested more than five directories below a skill/skills root, so valid skills that OpenCode would match via **/SKILL.md are missing from the provider snapshot and the $ picker. The depth guard cuts off discovery at an arbitrary level that does not reflect OpenCode's own matching semantics. Consider removing the fixed cap or raising it to a value that covers realistic skill layouts, and document the chosen bound if one is kept.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/OpenCodeSkills.ts around line 29:

`MAX_SKILL_DEPTH = 5` causes `collectSkillFiles` to silently drop any `SKILL.md` nested more than five directories below a `skill`/`skills` root, so valid skills that OpenCode would match via `**/SKILL.md` are missing from the provider snapshot and the `$` picker. The depth guard cuts off discovery at an arbitrary level that does not reflect OpenCode's own matching semantics. Consider removing the fixed cap or raising it to a value that covers realistic skill layouts, and document the chosen bound if one is kept.

@macroscopeapp

macroscopeapp Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

3 blocking correctness issues found. This PR introduces new OpenCode skill discovery functionality with ~300 lines of new code, including filesystem scanning and YAML parsing. New features introducing new capabilities warrant human review. Additionally, unresolved findings identify issues with external server handling and environment resolution that need attention.

You can customize Macroscope's approvability policy. Learn more.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fd5a0c3. Configure here.

const configHome =
xdgConfigHome.length > 0 ? path.resolve(xdgConfigHome) : path.join(NodeOS.homedir(), ".config");
return path.join(configHome, "opencode");
});

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.

Config dir replaces instead of adding

Medium Severity

resolveOpenCodeConfigDirPath treats OPENCODE_CONFIG_DIR as an exclusive replacement for the XDG config dir. OpenCode’s skill loader still scans both via Config.directories(), so when that env var is set, skills under the default ~/.config/opencode keep working in the runtime but disappear from the $ picker.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fd5a0c3. Configure here.

@t3dotgg

t3dotgg commented Aug 23, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

Closing this PR after an automated pass over open pull requests. OpenCode skill discovery already shipped in #3154.

@t3dotgg t3dotgg closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants