From 3aab7efe263ea181e1808b70233291f2ec0c324e Mon Sep 17 00:00:00 2001 From: Chris Yuan Date: Thu, 25 Jun 2026 12:49:32 +0800 Subject: [PATCH] fix(skill): provide directory context in all skill code paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill's base directory was missing or URL-encoded in two code paths, preventing the agent from resolving bundled resources (references/, scripts/) relative to the skill install directory. 1. System prompt used pathToFileURL, URL-encoding characters like # from git-tagged plugin cache dirs. Emit the plain filesystem path instead — same fix as #33580 but for the second occurrence in skill/index.ts fmt(). 2. Skills invoked as slash commands (/skill-name) injected only the raw SKILL.md content into the prompt with no directory context at all. The agent had no way to locate bundled files. Inject the base directory hint, matching the skill tool's output format. --- packages/opencode/src/command/index.ts | 11 ++++++++++- packages/opencode/src/skill/index.ts | 3 +-- packages/opencode/src/tool/skill.ts | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/command/index.ts b/packages/opencode/src/command/index.ts index 4c71ff577673..b59db7f40af4 100644 --- a/packages/opencode/src/command/index.ts +++ b/packages/opencode/src/command/index.ts @@ -1,4 +1,5 @@ import { LayerNode } from "@opencode-ai/core/effect/layer-node" +import path from "path" import { InstanceState } from "@/effect/instance-state" import { EffectBridge } from "@/effect/bridge" import type { InstanceContext } from "@/project/instance-context" @@ -132,12 +133,20 @@ export const layer = Layer.effect( for (const item of yield* skill.all()) { if (commands[item.name]) continue + // Built-in skills have no on-disk directory, so omit the base-directory hint. + const dir = item.location === "" ? undefined : path.dirname(item.location) commands[item.name] = { name: item.name, description: item.description, source: "skill", get template() { - return item.content + if (!dir) return item.content + return [ + item.content, + "", + `Base directory for this skill: ${dir}`, + "Relative paths in this skill (e.g., scripts/, references/) are relative to this base directory.", + ].join("\n") }, hints: [], } diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts index 08a88a215432..5e2040137711 100644 --- a/packages/opencode/src/skill/index.ts +++ b/packages/opencode/src/skill/index.ts @@ -1,6 +1,5 @@ import { LayerNode } from "@opencode-ai/core/effect/layer-node" import path from "path" -import { pathToFileURL } from "url" import { Effect, Layer, Context, Schema } from "effect" import { NamedError } from "@opencode-ai/core/util/error" import type { Agent } from "@/agent/agent" @@ -339,7 +338,7 @@ export function fmt(list: Info[], opts: { verbose: boolean }) { " ", ` ${skill.name}`, ` ${skill.description}`, - ` ${pathToFileURL(skill.location).href}`, + ` ${skill.location}`, " ", ]), "", diff --git a/packages/opencode/src/tool/skill.ts b/packages/opencode/src/tool/skill.ts index 9149bcc5ff0d..7a6306268e6f 100644 --- a/packages/opencode/src/tool/skill.ts +++ b/packages/opencode/src/tool/skill.ts @@ -51,7 +51,7 @@ export const SkillTool = Tool.define( info.content.trim(), "", `Base directory for this skill: ${base}`, - "Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.", + "Relative paths in this skill (e.g., scripts/, references/) are relative to this base directory.", "Note: file list is sampled.", "", "",