fix(server): report OpenCode skills in the provider snapshot - #5683
fix(server): report OpenCode skills in the provider snapshot#5683vraj-ai wants to merge 3 commits into
Conversation
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>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
| DEFAULT_OPENCODE_MODEL_CAPABILITIES, | ||
| ); | ||
| const connectedCount = inventoryExit.value.providerList.connected.length; | ||
| const skills = yield* discoverOpenCodeSkills(cwd, resolvedEnvironment); |
There was a problem hiding this comment.
🟠 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.
| 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"); |
There was a problem hiding this comment.
🟡 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().
| 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; | ||
|
|
||
| /** |
There was a problem hiding this comment.
🟡 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.
ApprovabilityVerdict: 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. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ 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"); | ||
| }); |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit fd5a0c3. Configure here.
|
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. |


Problem
The OpenCode provider snapshot never populates
skills, so the$picker is always empty for OpenCode models. Claude and Codex both list theirs —ClaudeProvidercallsdiscoverClaudeSkills,CodexProviderparsesskills/list— butOpenCodeProviderhas no skill code at all, soServerProvider.skillsfalls back to its[]default.Users with skills installed in OpenCode's own directories see them work through OpenCode's built-in
skilltool, 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
ClaudeSkillsdriver.I looked at reading the SDK instead (
client.skill.list()), but the local install path builds its inventory fromopencode models/opencode agent listand never starts a server — and there is noopencode skillCLI command. Using the SDK would mean spawning a server purely to probe, and would leave the common non-serverUrlinstall with no skills. Scanning the filesystem covers both install paths with no extra process.OpenCodeSkillsscans OpenCode's skill roots:<config dir>/skilland<config dir>/skills<cwd>/.opencode/skilland<cwd>/.opencode/skillshonouring
OPENCODE_CONFIG_DIRthenXDG_CONFIG_HOMEthen~/.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, andstatfollows 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 toFileSystem | Pathand provide those services inOpenCodeDriver, exactly asClaudeDriveralready does forcheckClaudeProviderStatus. Both were already inOpenCodeDriver's declared requirements.This does not touch
skills.pathsorskills.urlsfromopencode.json. The server is spawned withOPENCODE_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 failurespnpm typecheck— cleanvp lint/vp format --check— cleanskilldir and nested skills, project-wins collision, and skipping malformed/unnamed frontmatter and missing roots~/.config/opencode/skills), discovery returns all of them with correct paths and descriptions; before this change the snapshot reported noneOn 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
ClaudeSkillsrather 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 emptyskillsarray.Adds
discoverOpenCodeSkills, which scans user config (skill/skillsunder the resolved OpenCode config dir) and project workspace (.opencode/skill(s)), finds nestedSKILL.mdfiles, parses YAML frontmatter forname/description, skips bad entries, and lets project scope override user on name clashes.checkOpenCodeProviderStatuscalls that helper on successful probes and passes the result intobuildServerProvider.OpenCodeDriverwiresFileSystemandPathinto 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
discoverOpenCodeSkillsto walk user (~/.config/opencode/{skill,skills}) and project (.opencode/{skill,skills}) directories, parsing YAML frontmatter fromSKILL.mdfiles to extract name and description.namefield and are sorted alphabetically.checkOpenCodeProviderStatusto calldiscoverOpenCodeSkillsand include the resulting skills array in the returnedServerProviderDraft.FileSystemandPathservices to thecheckOpenCodeProviderStatuseffect inOpenCodeDriver.createto 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.