You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here is the directory structure of my skill:
skill-name/
├── SKILL.md
├── scripts/
├── references/
It's failing to resolve the paths correctly. Instead of looking inside the skill directory, it's treating the paths as relative to my workspace root, so it can't find anything in references/.
have you solve it?
Sorry, no. This is a bug in opencode, and without a solution to dynamically resolve relative paths to skill resources, there's no solution.
I will say the model is usually persistent enought to try a bunch of paths, and eventually finds the helper script, but it's annoying and is probably wasteful of tokens.
I noticed that this issue only occurs when triggering the skill as a slash command like /my-skill. When doing so, the command is expanded to content of .agents/skills/my-skill/SKILL.md in my prompt. Maybe OpenCode loses the connection to the actual skill here.
When the skill is triggered by the agent through its description, for example by saying „lets plan the next story“, paths can be resolved correctly. This can help as a workaround, too. But hopefully this will help fixing this bug.
I've been working on a project which I'll share publicly later that generates supplemental documentation for OpenCode. I won't go into details of that project here, but I've learned quite a bit about how skills work internally, and what the agent sees vs. what's presented to the user on the console.
Here's an excerpt. Skip below for SKILL.md suggestions to help the agent locate bundled resources. (Note this is based on an older tag of the source code, so the source code reference may have changed). The excerpt below was fully automated, generated by a combination of scripts and LLM.
The Agent's View: <skill_content>
When the agent invokes the skill tool, it receives a structured XML block. Understanding this block is critical for writing effective skills. Here is exactly what the agent sees:
<skill_contentname="my-skill">
# Skill: my-skill
[the full body of your SKILL.md, minus the frontmatter]
Base directory for this skill: file:///Users/you/.config/opencode/skills/my-skill/
Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.
Note: file list is sampled.
<skill_files>
<file>/Users/you/.config/opencode/skills/my-skill/scripts/helper.sh</file>
<file>/Users/you/.config/opencode/skills/my-skill/reference/api-spec.yaml</file>
</skill_files>
</skill_content>
Key observations for skill authors:
Your content comes first, followed by metadata about the skill's directory and files. The agent may read your instructions before it gets to the path hints.
The file list is sampled — only up to 10 files are shown, and SKILL.md itself is excluded. Large skills may not list all files. The agent will find unlisted files by searching.
File paths are absolute, not relative. The agent can execute them directly via bash without resolving relative paths.
Your SKILL.md content is emitted verbatim — no variable substitution, no macro expansion, no placeholder replacement of any kind (see next section).
Take away: the agent doesn't have any sort of variable or macro expansion that resolves to the skill location. No special SKILL_LOCATION/scripts/script.py syntax. However, it does have more context to work with than you realize. It gets a skill_content XML block with a nested skill_files XML tag. It's also explicitly told the "Base directory" of the skill, e.g.: /Users/user/projects/ProjectName/.opencode/skills/my-fancy-skill/.
You should be able to work with this information in your SKILL.md and the agent will know what you mean. Here's an example that's been working for me surprisingly well:
---name: dev-setupdescription: Set up development environment---
Run the development environment setup script.
Step 1:
> Note: BASE_DIRECTORY should be the "Base Directory" from your "<skill-content ... >" message.
Execute: `{BASE_DIRECTORY}/scripts/dev-setup.sh`
Of course rather than being preprocessed by the harness and expanded deterministically, this gets handled by the LLM probabilistically. So it's going to depend on your model and what else is in the conext window.
But play around with it, and see if you get good results! Report back positive and negative findings so followers of this issue can take away lessons learned, until we get a proper, templated "skill location" macro.
Oh another thing I've learned: there's A LOT you can ask the agent about what it "saw" that you didn't see, and it'll help you diagnose when things went wrong. For example you can say:
When I invoked "my-fancy-skill" you either couldn't find the helper script, or it took you several tries. I'd like to improve the skill. can you show me the "skill_content" XML block you received when I invoked it?
This is especially effective if you switch back to a mode like Plan so the agent doesn't jump straight to fixing the problem, like "oh you're right. Now that I've found the script I'll run it..."
Then you can back-and-forth with the agent on ideas that might improve the skill.
Also, often when the agent tries something and fails, it gets error messages in the context window that you don't see. You can ask it to show you those, especially if you interrupted right when the error happened.
Using the agent harness to improve the agent harness is an untapped superpower of these tools. Give it a try!
I noticed that this issue only occurs when triggering the skill as a slash command like /my-skill. When doing so, the command is expanded to content of .agents/skills/my-skill/SKILL.md in my prompt.
@micha149 yes! This is right! One of the things I’ve been learning in my source code spelunking is commands and skills work very differently for opencode, and when you invoke a skill this way it essentially becomes a command. I.e., the skill description is dumped directly into your prompt as if it were a slash command. More importantly, it does not get the XML block described above, so it gets NONE of that context from the skill_content block. Worse, I believe it also doesn’t get the $ARGUMENTS expansion commands get, so this method is the worst of both worlds.
DO NOT invoke skills as slash commands except for the simplest of skills.
That said, even with the normal invocation I still have some trouble with the resolution of helper scripts, just not quite as much.
Identified the root cause: the skill tool emits the base directory as a file:// URL via pathToFileURL(dir).href (packages/opencode/src/tool/skill.ts). When the path contains characters that get URL-encoded — e.g. # from a git-tagged plugin cache dir (...git#v1.3.0 → ...git%23v1.3.0) — the LLM derives a read-tool path with %23 but the filesystem has #, so read returns "File not found" and references appear missing.
The <file> entries right below already use path.resolve(dir, file.path) (plain paths), so the base directory should match them.
Fix in #33580 — emits dir directly instead of pathToFileURL(dir).href. Also resolves the file:/// prefix issue from #23885.
@rekram1-node I do not believe PR #33580 addresses this issue. I think many people here, including me, are seeing file paths bundled with skills fail to resolve in variety of much broader circumstances.
No doubht that PR addresses a bug, but I do not believe this issue is resolved.
@zcutlip You're right, and I want to make sure your work here isn't lost. Your comments in this thread were the most thorough tracing of this problem — the source-code spelunking into how skills work internally, the distinction between slash-command invocation vs. the skill tool, the observation about missing <skill_content> context in the command path, and the {CLAUDE_PLUGIN_ROOT} gap — all of that was essential to understanding why #33580 only fixed part of the problem.
I've opened #33786 to capture the two remaining bug code paths you and @micha149 identified:
The system prompt <available_skills> block still URL-encodes <location> (same pathToFileURL bug, second occurrence)
Skills invoked as slash commands get no directory context at all — just raw SKILL.md content with no base directory or file list
The third issue you raised — the lack of a deterministic ${SKILL_ROOT} macro (or equivalent) for skill authors to reliably reference bundled files — is a design-level feature request. That one needs the OpenCode team to weigh in on the approach, so it belongs in a separate feature discussion rather than a bug fix PR. I'd encourage you to open that one if you haven't already, since you've clearly thought through the design tradeoffs already.
Activity
github-actions commentedon Mar 20, 2026
This issue might be a duplicate of existing issues. Please check:
Both issues have been closed. It may be worth checking if the fix was applied and if this is a regression, or if your setup differs in some way.
util6 commentedon Mar 20, 2026
zcutlip commentedon Apr 8, 2026
Many claude skills use
${CLAUDE_PLUGIN_ROOT}, as in:${CLAUDE_PLUGIN_ROOT}/scripts/helper_script.sh.I don't think opencode supports this or something similar, but I don't know how to have a skill locate a script dynamicaly without it.
zol9527 commentedon Apr 14, 2026
Hi friend, have you solve it? Recently I use claude code & opencode both rencetly, but opencode can't read rules, scripts path properly.
zcutlip commentedon Apr 15, 2026
I will say the model is usually persistent enought to try a bunch of paths, and eventually finds the helper script, but it's annoying and is probably wasteful of tokens.
micha149 commentedon May 19, 2026
I noticed that this issue only occurs when triggering the skill as a slash command like
/my-skill. When doing so, the command is expanded to content of.agents/skills/my-skill/SKILL.mdin my prompt. Maybe OpenCode loses the connection to the actual skill here.When the skill is triggered by the agent through its description, for example by saying „lets plan the next story“, paths can be resolved correctly. This can help as a workaround, too. But hopefully this will help fixing this bug.
zcutlip commentedon May 30, 2026
I've been working on a project which I'll share publicly later that generates supplemental documentation for OpenCode. I won't go into details of that project here, but I've learned quite a bit about how skills work internally, and what the agent sees vs. what's presented to the user on the console.
Here's an excerpt. Skip below for SKILL.md suggestions to help the agent locate bundled resources. (Note this is based on an older tag of the source code, so the source code reference may have changed). The excerpt below was fully automated, generated by a combination of scripts and LLM.
The Agent's View:
<skill_content>When the agent invokes the
skilltool, it receives a structured XML block. Understanding this block is critical for writing effective skills. Here is exactly what the agent sees:Key observations for skill authors:
SKILL.mditself is excluded. Large skills may not list all files. The agent will find unlisted files by searching.Source:
packages/opencode/src/tool/skill.ts:23-72(executefunction),packages/opencode/src/tool/skill.txt(tool description)...hi human here again.
Take away: the agent doesn't have any sort of variable or macro expansion that resolves to the skill location. No special
SKILL_LOCATION/scripts/script.pysyntax. However, it does have more context to work with than you realize. It gets askill_contentXML block with a nestedskill_filesXML tag. It's also explicitly told the "Base directory" of the skill, e.g.:/Users/user/projects/ProjectName/.opencode/skills/my-fancy-skill/.You should be able to work with this information in your SKILL.md and the agent will know what you mean. Here's an example that's been working for me surprisingly well:
Of course rather than being preprocessed by the harness and expanded deterministically, this gets handled by the LLM probabilistically. So it's going to depend on your model and what else is in the conext window.
But play around with it, and see if you get good results! Report back positive and negative findings so followers of this issue can take away lessons learned, until we get a proper, templated "skill location" macro.
zcutlip commentedon May 30, 2026
Oh another thing I've learned: there's A LOT you can ask the agent about what it "saw" that you didn't see, and it'll help you diagnose when things went wrong. For example you can say:
This is especially effective if you switch back to a mode like Plan so the agent doesn't jump straight to fixing the problem, like "oh you're right. Now that I've found the script I'll run it..."
Then you can back-and-forth with the agent on ideas that might improve the skill.
Also, often when the agent tries something and fails, it gets error messages in the context window that you don't see. You can ask it to show you those, especially if you interrupted right when the error happened.
Using the agent harness to improve the agent harness is an untapped superpower of these tools. Give it a try!
zcutlip commentedon May 30, 2026
@micha149 yes! This is right! One of the things I’ve been learning in my source code spelunking is commands and skills work very differently for opencode, and when you invoke a skill this way it essentially becomes a command. I.e., the skill description is dumped directly into your prompt as if it were a slash command. More importantly, it does not get the XML block described above, so it gets NONE of that context from the
skill_contentblock. Worse, I believe it also doesn’t get the$ARGUMENTSexpansion commands get, so this method is the worst of both worlds.DO NOT invoke skills as slash commands except for the simplest of skills.
That said, even with the normal invocation I still have some trouble with the resolution of helper scripts, just not quite as much.
shyuan commentedon Jun 24, 2026
Identified the root cause: the
skilltool emits the base directory as afile://URL viapathToFileURL(dir).href(packages/opencode/src/tool/skill.ts). When the path contains characters that get URL-encoded — e.g.#from a git-tagged plugin cache dir (...git#v1.3.0→...git%23v1.3.0) — the LLM derives aread-tool path with%23but the filesystem has#, soreadreturns "File not found" and references appear missing.The
<file>entries right below already usepath.resolve(dir, file.path)(plain paths), so the base directory should match them.Fix in #33580 — emits
dirdirectly instead ofpathToFileURL(dir).href. Also resolves thefile:///prefix issue from #23885.util6 commentedon Jun 24, 2026
zcutlip commentedon Jun 24, 2026
@rekram1-node I do not believe PR #33580 addresses this issue. I think many people here, including me, are seeing file paths bundled with skills fail to resolve in variety of much broader circumstances.
No doubht that PR addresses a bug, but I do not believe this issue is resolved.
shyuan commentedon Jun 25, 2026
@zcutlip You're right, and I want to make sure your work here isn't lost. Your comments in this thread were the most thorough tracing of this problem — the source-code spelunking into how skills work internally, the distinction between slash-command invocation vs. the skill tool, the observation about missing
<skill_content>context in the command path, and the{CLAUDE_PLUGIN_ROOT}gap — all of that was essential to understanding why #33580 only fixed part of the problem.I've opened #33786 to capture the two remaining bug code paths you and @micha149 identified:
<available_skills>block still URL-encodes<location>(samepathToFileURLbug, second occurrence)PR #33787 fixes both.
The third issue you raised — the lack of a deterministic
${SKILL_ROOT}macro (or equivalent) for skill authors to reliably reference bundled files — is a design-level feature request. That one needs the OpenCode team to weigh in on the approach, so it belongs in a separate feature discussion rather than a bug fix PR. I'd encourage you to open that one if you haven't already, since you've clearly thought through the design tradeoffs already.