Skip to content

Commit f993541

Browse files
authored
Refactor to support multiple instances inside single opencode process (anomalyco#2360)
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
1 parent e2df3eb commit f993541

112 files changed

Lines changed: 4161 additions & 3017 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010
- AVOID `let` statements
1111
- PREFER single word variable names where possible
1212
- Use as many bun apis as possible like Bun.file()
13+
14+
## Debugging
15+
16+
- To test opencode in the `packages/opencode` directory you can run `bun dev`

packages/opencode/src/agent/agent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { App } from "../app/app"
21
import { Config } from "../config/config"
32
import z from "zod"
43
import { Provider } from "../provider/provider"
54
import { generateObject, type ModelMessage } from "ai"
65
import PROMPT_GENERATE from "./generate.txt"
76
import { SystemPrompt } from "../session/system"
7+
import { Instance } from "../project/instance"
88
import { mergeDeep } from "remeda"
99

1010
export namespace Agent {
@@ -36,7 +36,7 @@ export namespace Agent {
3636
})
3737
export type Info = z.infer<typeof Info>
3838

39-
const state = App.state("agent", async () => {
39+
const state = Instance.state(async () => {
4040
const cfg = await Config.get()
4141
const defaultTools = cfg.tools ?? {}
4242
const defaultPermission: Info["permission"] = {

packages/opencode/src/app/app.ts

Lines changed: 0 additions & 147 deletions
This file was deleted.

packages/opencode/src/bus/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { z, type ZodType } from "zod"
2-
import { App } from "../app/app"
32
import { Log } from "../util/log"
3+
import { Instance } from "../project/instance"
44

55
export namespace Bus {
66
const log = Log.create({ service: "bus" })
77
type Subscription = (event: any) => void
88

9-
const state = App.state("bus", () => {
9+
const state = Instance.state(() => {
1010
const subscriptions = new Map<any, Subscription[]>()
1111

1212
return {
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { App } from "../app/app"
2-
import { ConfigHooks } from "../config/hooks"
31
import { Format } from "../format"
42
import { LSP } from "../lsp"
53
import { Plugin } from "../plugin"
4+
import { Instance } from "../project/instance"
65
import { Share } from "../share/share"
76
import { Snapshot } from "../snapshot"
87

9-
export async function bootstrap<T>(input: App.Input, cb: (app: App.Info) => Promise<T>) {
10-
return App.provide(input, async (app) => {
8+
export async function bootstrap<T>(directory: string, cb: () => Promise<T>) {
9+
return Instance.provide(directory, async () => {
1110
await Plugin.init()
1211
Share.init()
1312
Format.init()
14-
ConfigHooks.init()
1513
LSP.init()
1614
Snapshot.init()
17-
18-
return cb(app)
15+
const result = await cb()
16+
await Instance.dispose()
17+
return result
1918
})
2019
}

packages/opencode/src/cli/cmd/agent.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,26 @@ import { Global } from "../../global"
55
import { Agent } from "../../agent/agent"
66
import path from "path"
77
import matter from "gray-matter"
8-
import { App } from "../../app/app"
8+
import { Instance } from "../../project/instance"
99

1010
const AgentCreateCommand = cmd({
1111
command: "create",
1212
describe: "create a new agent",
1313
async handler() {
14-
await App.provide({ cwd: process.cwd() }, async (app) => {
14+
await Instance.provide(process.cwd(), async () => {
1515
UI.empty()
1616
prompts.intro("Create agent")
17+
const project = Instance.project
1718

1819
let scope: "global" | "project" = "global"
19-
if (app.git) {
20+
if (project.vcs === "git") {
2021
const scopeResult = await prompts.select({
2122
message: "Location",
2223
options: [
2324
{
2425
label: "Current project",
2526
value: "project" as const,
26-
hint: app.path.root,
27+
hint: Instance.worktree,
2728
},
2829
{
2930
label: "Global",
@@ -116,7 +117,7 @@ const AgentCreateCommand = cmd({
116117

117118
const content = matter.stringify(generated.systemPrompt, frontmatter)
118119
const filePath = path.join(
119-
scope === "global" ? Global.Path.config : path.join(app.path.root, ".opencode"),
120+
scope === "global" ? Global.Path.config : path.join(Instance.worktree, ".opencode"),
120121
`agent`,
121122
`${generated.identifier}.md`,
122123
)

packages/opencode/src/cli/cmd/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import path from "path"
88
import os from "os"
99
import { Global } from "../../global"
1010
import { Plugin } from "../../plugin"
11-
import { App } from "../../app/app"
11+
import { Instance } from "../../project/instance"
1212

1313
export const AuthCommand = cmd({
1414
command: "auth",
@@ -74,7 +74,7 @@ export const AuthLoginCommand = cmd({
7474
type: "string",
7575
}),
7676
async handler(args) {
77-
await App.provide({ cwd: process.cwd() }, async () => {
77+
await Instance.provide(process.cwd(), async () => {
7878
UI.empty()
7979
prompts.intro("Add credential")
8080
if (args.url) {

packages/opencode/src/cli/cmd/debug/app.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/opencode/src/cli/cmd/debug/file.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const FileReadCommand = cmd({
1111
description: "File path to read",
1212
}),
1313
async handler(args) {
14-
await bootstrap({ cwd: process.cwd() }, async () => {
14+
await bootstrap(process.cwd(), async () => {
1515
const content = await File.read(args.path)
1616
console.log(content)
1717
})
@@ -22,7 +22,7 @@ const FileStatusCommand = cmd({
2222
command: "status",
2323
builder: (yargs) => yargs,
2424
async handler() {
25-
await bootstrap({ cwd: process.cwd() }, async () => {
25+
await bootstrap(process.cwd(), async () => {
2626
const status = await File.status()
2727
console.log(JSON.stringify(status, null, 2))
2828
})
@@ -38,7 +38,7 @@ const FileListCommand = cmd({
3838
description: "File path to list",
3939
}),
4040
async handler(args) {
41-
await bootstrap({ cwd: process.cwd() }, async () => {
41+
await bootstrap(process.cwd(), async () => {
4242
const files = await File.list(args.path)
4343
console.log(JSON.stringify(files, null, 2))
4444
})

packages/opencode/src/cli/cmd/debug/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Global } from "../../../global"
22
import { bootstrap } from "../../bootstrap"
33
import { cmd } from "../cmd"
4-
import { AppCommand } from "./app"
54
import { FileCommand } from "./file"
65
import { LSPCommand } from "./lsp"
76
import { RipgrepCommand } from "./ripgrep"
@@ -12,7 +11,6 @@ export const DebugCommand = cmd({
1211
command: "debug",
1312
builder: (yargs) =>
1413
yargs
15-
.command(AppCommand)
1614
.command(LSPCommand)
1715
.command(RipgrepCommand)
1816
.command(FileCommand)
@@ -22,7 +20,7 @@ export const DebugCommand = cmd({
2220
.command({
2321
command: "wait",
2422
async handler() {
25-
await bootstrap({ cwd: process.cwd() }, async () => {
23+
await bootstrap(process.cwd(), async () => {
2624
await new Promise((resolve) => setTimeout(resolve, 1_000 * 60 * 60 * 24))
2725
})
2826
},

0 commit comments

Comments
 (0)