Skip to content

Commit 367d21a

Browse files
authored
Merge branch 'dev' into feat/project-close-command-palette
2 parents 6189341 + 9c585bb commit 367d21a

6 files changed

Lines changed: 54 additions & 28 deletions

File tree

packages/opencode/src/session/prompt.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,12 @@ export namespace SessionPrompt {
650650
await Plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs })
651651

652652
// Build system prompt, adding structured output instruction if needed
653-
const system = [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())]
653+
const skills = await SystemPrompt.skills(agent)
654+
const system = [
655+
...(await SystemPrompt.environment(model)),
656+
...(skills ? [skills] : []),
657+
...(await InstructionPrompt.system()),
658+
]
654659
const format = lastUser.format ?? { type: "text" }
655660
if (format.type === "json_schema") {
656661
system.push(STRUCTURED_OUTPUT_SYSTEM_PROMPT)

packages/opencode/src/session/system.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import PROMPT_GEMINI from "./prompt/gemini.txt"
1010
import PROMPT_CODEX from "./prompt/codex_header.txt"
1111
import PROMPT_TRINITY from "./prompt/trinity.txt"
1212
import type { Provider } from "@/provider/provider"
13+
import type { Agent } from "@/agent/agent"
14+
import { PermissionNext } from "@/permission/next"
15+
import { Skill } from "@/skill"
1316

1417
export namespace SystemPrompt {
1518
export function instructions() {
@@ -34,6 +37,7 @@ export namespace SystemPrompt {
3437
`Here is some useful information about the environment you are running in:`,
3538
`<env>`,
3639
` Working directory: ${Instance.directory}`,
40+
` Workspace root folder: ${Instance.worktree}`,
3741
` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`,
3842
` Platform: ${process.platform}`,
3943
` Today's date: ${new Date().toDateString()}`,
@@ -51,4 +55,16 @@ export namespace SystemPrompt {
5155
].join("\n"),
5256
]
5357
}
58+
59+
export async function skills(agent: Agent.Info) {
60+
if (PermissionNext.disabled(["skill"], agent.permission).has("skill")) return
61+
62+
const list = await Skill.available(agent)
63+
64+
return [
65+
"Skills provide specialized instructions and workflows for specific tasks.",
66+
"Use the skill tool to load a skill when a task matches its description.",
67+
list.length === 0 ? "No skills are currently available." : "\n" + Skill.fmt(list),
68+
].join("\n")
69+
}
5470
}

packages/opencode/src/skill/skill.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import { Bus } from "@/bus"
1313
import { Session } from "@/session"
1414
import { Discovery } from "./discovery"
1515
import { Glob } from "../util/glob"
16+
import { pathToFileURL } from "url"
17+
import type { Agent } from "@/agent/agent"
18+
import { PermissionNext } from "@/permission/next"
1619

1720
export namespace Skill {
1821
const log = Log.create({ service: "skill" })
@@ -186,4 +189,24 @@ export namespace Skill {
186189
export async function dirs() {
187190
return state().then((x) => x.dirs)
188191
}
192+
193+
export async function available(agent?: Agent.Info) {
194+
const list = await all()
195+
if (!agent) return list
196+
return list.filter((skill) => PermissionNext.evaluate("skill", skill.name, agent.permission).action !== "deny")
197+
}
198+
199+
export function fmt(list: Info[]) {
200+
return [
201+
"<available_skills>",
202+
...list.flatMap((skill) => [
203+
` <skill>`,
204+
` <name>${skill.name}</name>`,
205+
` <description>${skill.description}</description>`,
206+
` <location>${pathToFileURL(skill.location).href}</location>`,
207+
` </skill>`,
208+
]),
209+
"</available_skills>",
210+
].join("\n")
211+
}
189212
}

packages/opencode/src/tool/skill.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,14 @@ import { pathToFileURL } from "url"
33
import z from "zod"
44
import { Tool } from "./tool"
55
import { Skill } from "../skill"
6-
import { PermissionNext } from "../permission/next"
76
import { Ripgrep } from "../file/ripgrep"
87
import { iife } from "@/util/iife"
98

109
export const SkillTool = Tool.define("skill", async (ctx) => {
11-
const skills = await Skill.all()
12-
13-
// Filter skills by agent permissions if agent provided
14-
const agent = ctx?.agent
15-
const accessibleSkills = agent
16-
? skills.filter((skill) => {
17-
const rule = PermissionNext.evaluate("skill", skill.name, agent.permission)
18-
return rule.action !== "deny"
19-
})
20-
: skills
10+
const list = await Skill.available(ctx?.agent)
2111

2212
const description =
23-
accessibleSkills.length === 0
13+
list.length === 0
2414
? "Load a specialized skill that provides domain-specific instructions and workflows. No skills are currently available."
2515
: [
2616
"Load a specialized skill that provides domain-specific instructions and workflows.",
@@ -34,18 +24,10 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
3424
"The following skills provide specialized sets of instructions for particular tasks",
3525
"Invoke this tool to load a skill when a task matches one of the available skills listed below:",
3626
"",
37-
"<available_skills>",
38-
...accessibleSkills.flatMap((skill) => [
39-
` <skill>`,
40-
` <name>${skill.name}</name>`,
41-
` <description>${skill.description}</description>`,
42-
` <location>${pathToFileURL(skill.location).href}</location>`,
43-
` </skill>`,
44-
]),
45-
"</available_skills>",
27+
Skill.fmt(list),
4628
].join("\n")
4729

48-
const examples = accessibleSkills
30+
const examples = list
4931
.map((skill) => `'${skill.name}'`)
5032
.slice(0, 3)
5133
.join(", ")
@@ -62,7 +44,7 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
6244
const skill = await Skill.get(params.name)
6345

6446
if (!skill) {
65-
const available = await Skill.all().then((x) => Object.keys(x).join(", "))
47+
const available = await Skill.all().then((x) => x.map((skill) => skill.name).join(", "))
6648
throw new Error(`Skill "${params.name}" not found. Available skills: ${available || "none"}`)
6749
}
6850

packages/web/src/content/docs/providers.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,7 @@ You can use any OpenAI-compatible provider with opencode. Most modern AI provide
18901890
```
18911891
18921892
Here are the configuration options:
1893-
- **npm**: AI SDK package to use, `@ai-sdk/openai-compatible` for OpenAI-compatible providers
1893+
- **npm**: AI SDK package to use, `@ai-sdk/openai-compatible` for OpenAI-compatible providers (for `/v1/chat/completions`). If your provider/model uses `/v1/responses`, use `@ai-sdk/openai`.
18941894
- **name**: Display name in UI.
18951895
- **models**: Available models.
18961896
- **options.baseURL**: API endpoint URL.
@@ -1957,5 +1957,5 @@ If you are having trouble with configuring a provider, check the following:
19571957
19581958
2. For custom providers, check the opencode config and:
19591959
- Make sure the provider ID used in the `/connect` command matches the ID in your opencode config.
1960-
- The right npm package is used for the provider. For example, use `@ai-sdk/cerebras` for Cerebras. And for all other OpenAI-compatible providers, use `@ai-sdk/openai-compatible`.
1960+
- The right npm package is used for the provider. For example, use `@ai-sdk/cerebras` for Cerebras. And for all other OpenAI-compatible providers, use `@ai-sdk/openai-compatible` (for `/v1/chat/completions`); if a model uses `/v1/responses`, use `@ai-sdk/openai`. For mixed setups under one provider, you can override per model via `provider.npm`.
19611961
- Check correct API endpoint is used in the `options.baseURL` field.

packages/web/src/content/docs/zh-cn/providers.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,7 +1845,7 @@ Vercel AI Gateway 允许你通过统一端点访问来自 OpenAI、Anthropic、G
18451845
```
18461846
18471847
以下是配置选项说明:
1848-
- **npm**:要使用的 AI SDK 包,对于 OpenAI 兼容的提供商使用 `@ai-sdk/openai-compatible`
1848+
- **npm**:要使用的 AI SDK 包,对于 OpenAI 兼容的提供商使用 `@ai-sdk/openai-compatible`(适用于 `/v1/chat/completions`)。如果你的提供商/模型走 `/v1/responses`,请使用 `@ai-sdk/openai`
18491849
- **name**:在 UI 中显示的名称。
18501850
- **models**:可用模型。
18511851
- **options.baseURL**:API 端点 URL。
@@ -1911,5 +1911,5 @@ Vercel AI Gateway 允许你通过统一端点访问来自 OpenAI、Anthropic、G
19111911
19121912
2. 对于自定义提供商,请检查 OpenCode 配置并确认:
19131913
- `/connect` 命令中使用的提供商 ID 与 OpenCode 配置中的 ID 一致。
1914-
- 使用了正确的 npm 包。例如,Cerebras 应使用 `@ai-sdk/cerebras`。对于其他所有 OpenAI 兼容的提供商,使用 `@ai-sdk/openai-compatible`
1914+
- 使用了正确的 npm 包。例如,Cerebras 应使用 `@ai-sdk/cerebras`。对于其他所有 OpenAI 兼容的提供商,使用 `@ai-sdk/openai-compatible``/v1/chat/completions`);如果模型走 `/v1/responses`,请使用 `@ai-sdk/openai`。同一 provider 混用时,可在模型下设置 `provider.npm` 覆盖默认值
19151915
- `options.baseURL` 字段中的 API 端点地址正确。

0 commit comments

Comments
 (0)