fix(opencode): preserve skill resource paths (#34052)

This commit is contained in:
Shoubhit Dash 2026-06-26 16:48:30 +05:30 committed by GitHub
commit ae853561cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 38 additions and 3 deletions

View file

@ -1,4 +1,5 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import path from "path"
import { InstanceState } from "@/effect/instance-state" import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect/bridge" import { EffectBridge } from "@/effect/bridge"
import type { InstanceContext } from "@/project/instance-context" import type { InstanceContext } from "@/project/instance-context"
@ -132,12 +133,19 @@ export const layer = Layer.effect(
for (const item of yield* skill.all()) { for (const item of yield* skill.all()) {
if (commands[item.name]) continue if (commands[item.name]) continue
const dir = item.location === "<built-in>" ? undefined : path.dirname(item.location)
commands[item.name] = { commands[item.name] = {
name: item.name, name: item.name,
description: item.description, description: item.description,
source: "skill", source: "skill",
get template() { 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: [], hints: [],
} }

View file

@ -1,6 +1,5 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import path from "path" import path from "path"
import { pathToFileURL } from "url"
import { Effect, Layer, Context, Schema } from "effect" import { Effect, Layer, Context, Schema } from "effect"
import { NamedError } from "@opencode-ai/core/util/error" import { NamedError } from "@opencode-ai/core/util/error"
import type { Agent } from "@/agent/agent" import type { Agent } from "@/agent/agent"
@ -17,6 +16,7 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
import { Glob } from "@opencode-ai/core/util/glob" import { Glob } from "@opencode-ai/core/util/glob"
import { Discovery } from "./discovery" import { Discovery } from "./discovery"
import { isRecord } from "@/util/record" import { isRecord } from "@/util/record"
import { escapeHtml } from "@/util/html"
const CLAUDE_EXTERNAL_DIR = ".claude" const CLAUDE_EXTERNAL_DIR = ".claude"
const AGENTS_EXTERNAL_DIR = ".agents" const AGENTS_EXTERNAL_DIR = ".agents"
@ -339,7 +339,7 @@ export function fmt(list: Info[], opts: { verbose: boolean }) {
" <skill>", " <skill>",
` <name>${skill.name}</name>`, ` <name>${skill.name}</name>`,
` <description>${skill.description}</description>`, ` <description>${skill.description}</description>`,
` <location>${pathToFileURL(skill.location).href}</location>`, ` <location>${escapeHtml(skill.location)}</location>`,
" </skill>", " </skill>",
]), ]),
"</available_skills>", "</available_skills>",

View file

@ -77,6 +77,33 @@ const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) =>
) )
describe("skill", () => { describe("skill", () => {
it.effect("formats verbose locations as XML-safe filesystem paths", () =>
Effect.sync(() => {
const output = Skill.fmt(
[
{
name: "tagged-skill",
description: "A tagged skill.",
location: "/tmp/plugin.git#v1.3.0/SKILL.md",
content: "",
},
{
name: "built-in-skill",
description: "A built-in skill.",
location: "<built-in>",
content: "",
},
],
{ verbose: true },
)
expect(output).toContain("<location>/tmp/plugin.git#v1.3.0/SKILL.md</location>")
expect(output).toContain("<location>&lt;built-in&gt;</location>")
expect(output).not.toContain("file://")
expect(output).not.toContain("%23")
}),
)
it.live("discovers skills from .opencode/skill/ directory", () => it.live("discovers skills from .opencode/skill/ directory", () =>
provideTmpdirInstance( provideTmpdirInstance(
(dir) => (dir) =>