From 0f6bc8ae71d18645212cb954bd210f047b5416d8 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Wed, 11 Mar 2026 10:24:55 -0500 Subject: [PATCH] tweak: adjust way skills are presented to agent to increase likelyhood of skill invocations. (#17053) --- packages/opencode/src/session/prompt.ts | 7 ++++++- packages/opencode/src/session/system.ts | 16 ++++++++++++++ packages/opencode/src/skill/skill.ts | 23 ++++++++++++++++++++ packages/opencode/src/tool/skill.ts | 28 +++++-------------------- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 655afd2b1..54adf1104 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -650,7 +650,12 @@ export namespace SessionPrompt { await Plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs }) // Build system prompt, adding structured output instruction if needed - const system = [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())] + const skills = await SystemPrompt.skills(agent) + const system = [ + ...(await SystemPrompt.environment(model)), + ...(skills ? [skills] : []), + ...(await InstructionPrompt.system()), + ] const format = lastUser.format ?? { type: "text" } if (format.type === "json_schema") { system.push(STRUCTURED_OUTPUT_SYSTEM_PROMPT) diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index a61dd8cba..0f0f6b51b 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -10,6 +10,9 @@ import PROMPT_GEMINI from "./prompt/gemini.txt" import PROMPT_CODEX from "./prompt/codex_header.txt" import PROMPT_TRINITY from "./prompt/trinity.txt" import type { Provider } from "@/provider/provider" +import type { Agent } from "@/agent/agent" +import { PermissionNext } from "@/permission/next" +import { Skill } from "@/skill" export namespace SystemPrompt { export function instructions() { @@ -34,6 +37,7 @@ export namespace SystemPrompt { `Here is some useful information about the environment you are running in:`, ``, ` Working directory: ${Instance.directory}`, + ` Workspace root folder: ${Instance.worktree}`, ` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`, ` Platform: ${process.platform}`, ` Today's date: ${new Date().toDateString()}`, @@ -51,4 +55,16 @@ export namespace SystemPrompt { ].join("\n"), ] } + + export async function skills(agent: Agent.Info) { + if (PermissionNext.disabled(["skill"], agent.permission).has("skill")) return + + const list = await Skill.available(agent) + + return [ + "Skills provide specialized instructions and workflows for specific tasks.", + "Use the skill tool to load a skill when a task matches its description.", + list.length === 0 ? "No skills are currently available." : "\n" + Skill.fmt(list), + ].join("\n") + } } diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index c474c94dd..09cc787c8 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -13,6 +13,9 @@ import { Bus } from "@/bus" import { Session } from "@/session" import { Discovery } from "./discovery" import { Glob } from "../util/glob" +import { pathToFileURL } from "url" +import type { Agent } from "@/agent/agent" +import { PermissionNext } from "@/permission/next" export namespace Skill { const log = Log.create({ service: "skill" }) @@ -186,4 +189,24 @@ export namespace Skill { export async function dirs() { return state().then((x) => x.dirs) } + + export async function available(agent?: Agent.Info) { + const list = await all() + if (!agent) return list + return list.filter((skill) => PermissionNext.evaluate("skill", skill.name, agent.permission).action !== "deny") + } + + export function fmt(list: Info[]) { + return [ + "", + ...list.flatMap((skill) => [ + ` `, + ` ${skill.name}`, + ` ${skill.description}`, + ` ${pathToFileURL(skill.location).href}`, + ` `, + ]), + "", + ].join("\n") + } } diff --git a/packages/opencode/src/tool/skill.ts b/packages/opencode/src/tool/skill.ts index 8fcfb592d..6d2a48b0e 100644 --- a/packages/opencode/src/tool/skill.ts +++ b/packages/opencode/src/tool/skill.ts @@ -3,24 +3,14 @@ import { pathToFileURL } from "url" import z from "zod" import { Tool } from "./tool" import { Skill } from "../skill" -import { PermissionNext } from "../permission/next" import { Ripgrep } from "../file/ripgrep" import { iife } from "@/util/iife" export const SkillTool = Tool.define("skill", async (ctx) => { - const skills = await Skill.all() - - // Filter skills by agent permissions if agent provided - const agent = ctx?.agent - const accessibleSkills = agent - ? skills.filter((skill) => { - const rule = PermissionNext.evaluate("skill", skill.name, agent.permission) - return rule.action !== "deny" - }) - : skills + const list = await Skill.available(ctx?.agent) const description = - accessibleSkills.length === 0 + list.length === 0 ? "Load a specialized skill that provides domain-specific instructions and workflows. No skills are currently available." : [ "Load a specialized skill that provides domain-specific instructions and workflows.", @@ -34,18 +24,10 @@ export const SkillTool = Tool.define("skill", async (ctx) => { "The following skills provide specialized sets of instructions for particular tasks", "Invoke this tool to load a skill when a task matches one of the available skills listed below:", "", - "", - ...accessibleSkills.flatMap((skill) => [ - ` `, - ` ${skill.name}`, - ` ${skill.description}`, - ` ${pathToFileURL(skill.location).href}`, - ` `, - ]), - "", + Skill.fmt(list), ].join("\n") - const examples = accessibleSkills + const examples = list .map((skill) => `'${skill.name}'`) .slice(0, 3) .join(", ") @@ -62,7 +44,7 @@ export const SkillTool = Tool.define("skill", async (ctx) => { const skill = await Skill.get(params.name) if (!skill) { - const available = await Skill.all().then((x) => Object.keys(x).join(", ")) + const available = await Skill.all().then((x) => x.map((skill) => skill.name).join(", ")) throw new Error(`Skill "${params.name}" not found. Available skills: ${available || "none"}`) }