summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-30 21:01:06 -0400
committerGitHub <[email protected]>2026-04-30 21:01:06 -0400
commit668d77bb4e5955eb56a81b3db13ea1dd74400cc2 (patch)
tree90f75d02f1d614d469910fbb17cf1d883d77da94
parent5c2e06f353eb00e1c4576ef24fb208eb3af935f8 (diff)
downloadopencode-668d77bb4e5955eb56a81b3db13ea1dd74400cc2.tar.gz
opencode-668d77bb4e5955eb56a81b3db13ea1dd74400cc2.zip
refactor(tool): yield InstanceState context (#25199)
-rw-r--r--packages/opencode/src/session/prompt.ts6
-rw-r--r--packages/opencode/src/session/session.ts8
-rw-r--r--packages/opencode/src/tool/apply_patch.ts20
-rw-r--r--packages/opencode/src/tool/bash.ts21
-rw-r--r--packages/opencode/src/tool/edit.ts11
-rw-r--r--packages/opencode/src/tool/lsp.ts7
-rw-r--r--packages/opencode/src/tool/plan.ts5
-rw-r--r--packages/opencode/src/tool/write.ts9
8 files changed, 51 insertions, 36 deletions
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 155b86b58..58edffe3f 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -256,7 +256,8 @@ export const layer = Layer.effect(
const assistantMessage = input.messages.findLast((msg) => msg.info.role === "assistant")
if (input.agent.name !== "plan" && assistantMessage?.info.agent === "plan") {
- const plan = Session.plan(input.session)
+ const ctx = yield* InstanceState.context
+ const plan = Session.plan(input.session, ctx)
if (!(yield* fsys.existsSafe(plan))) return input.messages
const part = yield* sessions.updatePart({
id: PartID.ascending(),
@@ -272,7 +273,8 @@ export const layer = Layer.effect(
if (input.agent.name !== "plan" || assistantMessage?.info.agent === "plan") return input.messages
- const plan = Session.plan(input.session)
+ const ctx = yield* InstanceState.context
+ const plan = Session.plan(input.session, ctx)
const exists = yield* fsys.existsSafe(plan)
if (!exists) yield* fsys.ensureDir(path.dirname(plan)).pipe(Effect.catch(Effect.die))
const part = yield* sessions.updatePart({
diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts
index 5534976e3..7e6016b87 100644
--- a/packages/opencode/src/session/session.ts
+++ b/packages/opencode/src/session/session.ts
@@ -26,7 +26,7 @@ import { ProjectTable } from "../project/project.sql"
import { Storage } from "@/storage/storage"
import * as Log from "@opencode-ai/core/util/log"
import { MessageV2 } from "./message-v2"
-import { Instance } from "../project/instance"
+import { Instance, type InstanceContext } from "../project/instance"
import { InstanceState } from "@/effect/instance-state"
import { Snapshot } from "@/snapshot"
import { ProjectID } from "../project/schema"
@@ -311,9 +311,9 @@ export const Event = {
),
}
-export function plan(input: { slug: string; time: { created: number } }) {
- const base = Instance.project.vcs
- ? path.join(Instance.worktree, ".opencode", "plans")
+export function plan(input: { slug: string; time: { created: number } }, instance: InstanceContext) {
+ const base = instance.project.vcs
+ ? path.join(instance.worktree, ".opencode", "plans")
: path.join(Global.Path.data, "plans")
return path.join(base, [input.time.created, input.slug].join("-") + ".md")
}
diff --git a/packages/opencode/src/tool/apply_patch.ts b/packages/opencode/src/tool/apply_patch.ts
index 2de18ad08..916e11f1e 100644
--- a/packages/opencode/src/tool/apply_patch.ts
+++ b/packages/opencode/src/tool/apply_patch.ts
@@ -3,7 +3,7 @@ import { Effect, Schema } from "effect"
import * as Tool from "./tool"
import { Bus } from "../bus"
import { FileWatcher } from "../file/watcher"
-import { Instance } from "../project/instance"
+import { InstanceState } from "@/effect/instance-state"
import { Patch } from "../patch"
import { createTwoFilesPatch, diffLines } from "diff"
import { assertExternalDirectoryEffect } from "./external-directory"
@@ -52,6 +52,8 @@ export const ApplyPatchTool = Tool.define(
return yield* Effect.fail(new Error("apply_patch verification failed: no hunks found"))
}
+ const instance = yield* InstanceState.context
+
// Validate file paths and check permissions
const fileChanges: Array<{
filePath: string
@@ -68,7 +70,7 @@ export const ApplyPatchTool = Tool.define(
let totalDiff = ""
for (const hunk of hunks) {
- const filePath = path.resolve(Instance.directory, hunk.path)
+ const filePath = path.resolve(instance.directory, hunk.path)
yield* assertExternalDirectoryEffect(ctx, filePath)
switch (hunk.type) {
@@ -133,7 +135,7 @@ export const ApplyPatchTool = Tool.define(
if (change.removed) deletions += change.count || 0
}
- const movePath = hunk.move_path ? path.resolve(Instance.directory, hunk.move_path) : undefined
+ const movePath = hunk.move_path ? path.resolve(instance.directory, hunk.move_path) : undefined
yield* assertExternalDirectoryEffect(ctx, movePath)
fileChanges.push({
@@ -187,7 +189,7 @@ export const ApplyPatchTool = Tool.define(
// Build per-file metadata for UI rendering (used for both permission and result)
const files = fileChanges.map((change) => ({
filePath: change.filePath,
- relativePath: path.relative(Instance.worktree, change.movePath ?? change.filePath).replaceAll("\\", "/"),
+ relativePath: path.relative(instance.worktree, change.movePath ?? change.filePath).replaceAll("\\", "/"),
type: change.type,
patch: change.diff,
additions: change.additions,
@@ -196,7 +198,7 @@ export const ApplyPatchTool = Tool.define(
}))
// Check permissions if needed
- const relativePaths = fileChanges.map((c) => path.relative(Instance.worktree, c.filePath).replaceAll("\\", "/"))
+ const relativePaths = fileChanges.map((c) => path.relative(instance.worktree, c.filePath).replaceAll("\\", "/"))
yield* ctx.ask({
permission: "edit",
patterns: relativePaths,
@@ -267,13 +269,13 @@ export const ApplyPatchTool = Tool.define(
// Generate output summary
const summaryLines = fileChanges.map((change) => {
if (change.type === "add") {
- return `A ${path.relative(Instance.worktree, change.filePath).replaceAll("\\", "/")}`
+ return `A ${path.relative(instance.worktree, change.filePath).replaceAll("\\", "/")}`
}
if (change.type === "delete") {
- return `D ${path.relative(Instance.worktree, change.filePath).replaceAll("\\", "/")}`
+ return `D ${path.relative(instance.worktree, change.filePath).replaceAll("\\", "/")}`
}
const target = change.movePath ?? change.filePath
- return `M ${path.relative(Instance.worktree, target).replaceAll("\\", "/")}`
+ return `M ${path.relative(instance.worktree, target).replaceAll("\\", "/")}`
})
let output = `Success. Updated the following files:\n${summaryLines.join("\n")}`
@@ -282,7 +284,7 @@ export const ApplyPatchTool = Tool.define(
const target = change.movePath ?? change.filePath
const block = LSP.Diagnostic.report(target, diagnostics[AppFileSystem.normalizePath(target)] ?? [])
if (!block) continue
- const rel = path.relative(Instance.worktree, target).replaceAll("\\", "/")
+ const rel = path.relative(instance.worktree, target).replaceAll("\\", "/")
output += `\n\nLSP errors detected in ${rel}, please fix:\n${block}`
}
diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts
index c32c3963b..c50b259f7 100644
--- a/packages/opencode/src/tool/bash.ts
+++ b/packages/opencode/src/tool/bash.ts
@@ -6,7 +6,7 @@ import * as Tool from "./tool"
import path from "path"
import DESCRIPTION from "./bash.txt"
import * as Log from "@opencode-ai/core/util/log"
-import { Instance } from "../project/instance"
+import { Instance, type InstanceContext } from "../project/instance"
import { lazy } from "@/util/lazy"
import { Language, type Node } from "web-tree-sitter"
@@ -363,7 +363,13 @@ export const BashTool = Tool.define(
return yield* resolvePath(next, cwd, shell)
})
- const collect = Effect.fn("BashTool.collect")(function* (root: Node, cwd: string, ps: boolean, shell: string) {
+ const collect = Effect.fn("BashTool.collect")(function* (
+ root: Node,
+ cwd: string,
+ ps: boolean,
+ shell: string,
+ instance: InstanceContext,
+ ) {
const scan: Scan = {
dirs: new Set<string>(),
patterns: new Set<string>(),
@@ -379,7 +385,7 @@ export const BashTool = Tool.define(
for (const arg of pathArgs(command, ps)) {
const resolved = yield* argPath(arg, cwd, ps, shell)
log.info("resolved path", { arg, resolved })
- if (!resolved || Instance.containsPath(resolved)) continue
+ if (!resolved || Instance.containsPath(resolved, instance)) continue
const dir = (yield* fs.isDir(resolved)) ? resolved : path.dirname(resolved)
scan.dirs.add(dir)
}
@@ -589,9 +595,10 @@ export const BashTool = Tool.define(
parameters: Parameters,
execute: (params: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) =>
Effect.gen(function* () {
+ const executeInstance = yield* InstanceState.context
const cwd = params.workdir
- ? yield* resolvePath(params.workdir, Instance.directory, shell)
- : Instance.directory
+ ? yield* resolvePath(params.workdir, executeInstance.directory, shell)
+ : executeInstance.directory
if (params.timeout !== undefined && params.timeout < 0) {
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
}
@@ -602,8 +609,8 @@ export const BashTool = Tool.define(
const tree = yield* Effect.acquireRelease(parse(params.command, ps), (tree) =>
Effect.sync(() => tree.delete()),
)
- const scan = yield* collect(tree.rootNode, cwd, ps, shell)
- if (!Instance.containsPath(cwd)) scan.dirs.add(cwd)
+ const scan = yield* collect(tree.rootNode, cwd, ps, shell, executeInstance)
+ if (!Instance.containsPath(cwd, executeInstance)) scan.dirs.add(cwd)
yield* ask(ctx, scan)
}),
)
diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts
index 2463d48fa..ea3aac348 100644
--- a/packages/opencode/src/tool/edit.ts
+++ b/packages/opencode/src/tool/edit.ts
@@ -13,7 +13,7 @@ import { File } from "../file"
import { FileWatcher } from "../file/watcher"
import { Bus } from "../bus"
import { Format } from "../format"
-import { Instance } from "../project/instance"
+import { InstanceState } from "@/effect/instance-state"
import { Snapshot } from "@/snapshot"
import { assertExternalDirectoryEffect } from "./external-directory"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
@@ -76,9 +76,10 @@ export const EditTool = Tool.define(
throw new Error("No changes to apply: oldString and newString are identical.")
}
+ const instance = yield* InstanceState.context
const filePath = path.isAbsolute(params.filePath)
? params.filePath
- : path.join(Instance.directory, params.filePath)
+ : path.join(instance.directory, params.filePath)
yield* assertExternalDirectoryEffect(ctx, filePath)
let diff = ""
@@ -96,7 +97,7 @@ export const EditTool = Tool.define(
diff = trimDiff(createTwoFilesPatch(filePath, filePath, contentOld, contentNew))
yield* ctx.ask({
permission: "edit",
- patterns: [path.relative(Instance.worktree, filePath)],
+ patterns: [path.relative(instance.worktree, filePath)],
always: ["*"],
metadata: {
filepath: filePath,
@@ -139,7 +140,7 @@ export const EditTool = Tool.define(
)
yield* ctx.ask({
permission: "edit",
- patterns: [path.relative(Instance.worktree, filePath)],
+ patterns: [path.relative(instance.worktree, filePath)],
always: ["*"],
metadata: {
filepath: filePath,
@@ -201,7 +202,7 @@ export const EditTool = Tool.define(
diff,
filediff,
},
- title: `${path.relative(Instance.worktree, filePath)}`,
+ title: `${path.relative(instance.worktree, filePath)}`,
output,
}
}),
diff --git a/packages/opencode/src/tool/lsp.ts b/packages/opencode/src/tool/lsp.ts
index 3a555c2ce..6f1532ca0 100644
--- a/packages/opencode/src/tool/lsp.ts
+++ b/packages/opencode/src/tool/lsp.ts
@@ -3,7 +3,7 @@ import * as Tool from "./tool"
import path from "path"
import { LSP } from "@/lsp/lsp"
import DESCRIPTION from "./lsp.txt"
-import { Instance } from "../project/instance"
+import { InstanceState } from "@/effect/instance-state"
import { pathToFileURL } from "url"
import { assertExternalDirectoryEffect } from "./external-directory"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
@@ -44,7 +44,8 @@ export const LspTool = Tool.define(
parameters: Parameters,
execute: (args: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) =>
Effect.gen(function* () {
- const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(Instance.directory, args.filePath)
+ const instance = yield* InstanceState.context
+ const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(instance.directory, args.filePath)
yield* assertExternalDirectoryEffect(ctx, file)
const meta =
args.operation === "workspaceSymbol"
@@ -61,7 +62,7 @@ export const LspTool = Tool.define(
const uri = pathToFileURL(file).href
const position = { file, line: args.line - 1, character: args.character - 1 }
- const relPath = path.relative(Instance.worktree, file)
+ const relPath = path.relative(instance.worktree, file)
const detail =
args.operation === "workspaceSymbol"
? ""
diff --git a/packages/opencode/src/tool/plan.ts b/packages/opencode/src/tool/plan.ts
index dc49ef48b..d5195376b 100644
--- a/packages/opencode/src/tool/plan.ts
+++ b/packages/opencode/src/tool/plan.ts
@@ -5,7 +5,7 @@ import { Question } from "../question"
import { Session } from "@/session/session"
import { MessageV2 } from "../session/message-v2"
import { Provider } from "@/provider/provider"
-import { Instance } from "../project/instance"
+import { InstanceState } from "@/effect/instance-state"
import { type SessionID, MessageID, PartID } from "../session/schema"
import EXIT_DESCRIPTION from "./plan-exit.txt"
@@ -30,8 +30,9 @@ export const PlanExitTool = Tool.define(
parameters: Parameters,
execute: (_params: {}, ctx: Tool.Context) =>
Effect.gen(function* () {
+ const instance = yield* InstanceState.context
const info = yield* session.get(ctx.sessionID)
- const plan = path.relative(Instance.worktree, Session.plan(info))
+ const plan = path.relative(instance.worktree, Session.plan(info, instance))
const answers = yield* question.ask({
sessionID: ctx.sessionID,
questions: [
diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts
index 09c3a3805..c2be73ab1 100644
--- a/packages/opencode/src/tool/write.ts
+++ b/packages/opencode/src/tool/write.ts
@@ -10,7 +10,7 @@ import { File } from "../file"
import { FileWatcher } from "../file/watcher"
import { Format } from "../format"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
-import { Instance } from "../project/instance"
+import { InstanceState } from "@/effect/instance-state"
import { trimDiff } from "./edit"
import { assertExternalDirectoryEffect } from "./external-directory"
import * as Bom from "@/util/bom"
@@ -37,9 +37,10 @@ export const WriteTool = Tool.define(
parameters: Parameters,
execute: (params: { content: string; filePath: string }, ctx: Tool.Context) =>
Effect.gen(function* () {
+ const instance = yield* InstanceState.context
const filepath = path.isAbsolute(params.filePath)
? params.filePath
- : path.join(Instance.directory, params.filePath)
+ : path.join(instance.directory, params.filePath)
yield* assertExternalDirectoryEffect(ctx, filepath)
const exists = yield* fs.existsSafe(filepath)
@@ -52,7 +53,7 @@ export const WriteTool = Tool.define(
const diff = trimDiff(createTwoFilesPatch(filepath, filepath, contentOld, contentNew))
yield* ctx.ask({
permission: "edit",
- patterns: [path.relative(Instance.worktree, filepath)],
+ patterns: [path.relative(instance.worktree, filepath)],
always: ["*"],
metadata: {
filepath,
@@ -89,7 +90,7 @@ export const WriteTool = Tool.define(
}
return {
- title: path.relative(Instance.worktree, filepath),
+ title: path.relative(instance.worktree, filepath),
metadata: {
diagnostics,
filepath,