This release has a bunch of minor breaking changes if you are using opencode plugins or sdk 1. storage events have been removed (we might bring this back but had some issues) 2. concept of `app` is gone - there is a new concept called `project` and endpoints to list projects and get the current project 3. plugin receives `directory` which is cwd and `worktree` which is where the root of the project is if it's a git repo 4. the session.chat function has been renamed to session.prompt in sdk. it no longer requires model to be passed in (model is now an object) 5. every endpoint takes an optional `directory` parameter to operate as though opencode is running in that directory
134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
import { Ripgrep } from "../file/ripgrep"
|
|
import { Global } from "../global"
|
|
import { Filesystem } from "../util/filesystem"
|
|
import { Config } from "../config/config"
|
|
|
|
import { Instance } from "../project/instance"
|
|
import path from "path"
|
|
import os from "os"
|
|
|
|
import PROMPT_ANTHROPIC from "./prompt/anthropic.txt"
|
|
import PROMPT_ANTHROPIC_WITHOUT_TODO from "./prompt/qwen.txt"
|
|
import PROMPT_BEAST from "./prompt/beast.txt"
|
|
import PROMPT_GEMINI from "./prompt/gemini.txt"
|
|
import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt"
|
|
import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
|
|
import PROMPT_TITLE from "./prompt/title.txt"
|
|
import PROMPT_COPILOT_GPT_5 from "./prompt/copilot-gpt-5.txt"
|
|
|
|
export namespace SystemPrompt {
|
|
export function header(providerID: string) {
|
|
if (providerID.includes("anthropic")) return [PROMPT_ANTHROPIC_SPOOF.trim()]
|
|
return []
|
|
}
|
|
|
|
export function provider(modelID: string) {
|
|
if (modelID.includes("gpt-5")) return [PROMPT_COPILOT_GPT_5]
|
|
if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST]
|
|
if (modelID.includes("gemini-")) return [PROMPT_GEMINI]
|
|
if (modelID.includes("claude")) return [PROMPT_ANTHROPIC]
|
|
return [PROMPT_ANTHROPIC_WITHOUT_TODO]
|
|
}
|
|
|
|
export async function environment() {
|
|
const project = Instance.project
|
|
return [
|
|
[
|
|
`Here is some useful information about the environment you are running in:`,
|
|
`<env>`,
|
|
` Working directory: ${Instance.directory}`,
|
|
` Is directory a git repo: ${project.vcs === "git" ? "yes" : "no"}`,
|
|
` Platform: ${process.platform}`,
|
|
` Today's date: ${new Date().toDateString()}`,
|
|
`</env>`,
|
|
`<project>`,
|
|
` ${
|
|
project.vcs === "git"
|
|
? await Ripgrep.tree({
|
|
cwd: Instance.directory,
|
|
limit: 200,
|
|
})
|
|
: ""
|
|
}`,
|
|
`</project>`,
|
|
].join("\n"),
|
|
]
|
|
}
|
|
|
|
const LOCAL_RULE_FILES = [
|
|
"AGENTS.md",
|
|
"CLAUDE.md",
|
|
"CONTEXT.md", // deprecated
|
|
]
|
|
const GLOBAL_RULE_FILES = [
|
|
path.join(Global.Path.config, "AGENTS.md"),
|
|
path.join(os.homedir(), ".claude", "CLAUDE.md"),
|
|
]
|
|
|
|
export async function custom() {
|
|
const config = await Config.get()
|
|
const paths = new Set<string>()
|
|
|
|
for (const localRuleFile of LOCAL_RULE_FILES) {
|
|
const matches = await Filesystem.findUp(localRuleFile, Instance.directory, Instance.worktree)
|
|
if (matches.length > 0) {
|
|
matches.forEach((path) => paths.add(path))
|
|
break
|
|
}
|
|
}
|
|
|
|
for (const globalRuleFile of GLOBAL_RULE_FILES) {
|
|
if (await Bun.file(globalRuleFile).exists()) {
|
|
paths.add(globalRuleFile)
|
|
break
|
|
}
|
|
}
|
|
|
|
if (config.instructions) {
|
|
for (let instruction of config.instructions) {
|
|
if (instruction.startsWith("~/")) {
|
|
instruction = path.join(os.homedir(), instruction.slice(2))
|
|
}
|
|
let matches: string[] = []
|
|
if (path.isAbsolute(instruction)) {
|
|
matches = await Array.fromAsync(
|
|
new Bun.Glob(path.basename(instruction)).scan({
|
|
cwd: path.dirname(instruction),
|
|
absolute: true,
|
|
onlyFiles: true,
|
|
}),
|
|
).catch(() => [])
|
|
} else {
|
|
matches = await Filesystem.globUp(instruction, Instance.directory, Instance.worktree).catch(() => [])
|
|
}
|
|
matches.forEach((path) => paths.add(path))
|
|
}
|
|
}
|
|
|
|
const found = Array.from(paths).map((p) =>
|
|
Bun.file(p)
|
|
.text()
|
|
.catch(() => ""),
|
|
)
|
|
return Promise.all(found).then((result) => result.filter(Boolean))
|
|
}
|
|
|
|
export function summarize(providerID: string) {
|
|
switch (providerID) {
|
|
case "anthropic":
|
|
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_SUMMARIZE]
|
|
default:
|
|
return [PROMPT_SUMMARIZE]
|
|
}
|
|
}
|
|
|
|
export function title(providerID: string) {
|
|
switch (providerID) {
|
|
case "anthropic":
|
|
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_TITLE]
|
|
default:
|
|
return [PROMPT_TITLE]
|
|
}
|
|
}
|
|
}
|