From 816f5e526bbc5f2e8ce46d9e8d87f879275d22ef Mon Sep 17 00:00:00 2001 From: James Long Date: Fri, 15 May 2026 15:33:00 -0400 Subject: [PATCH] tool for writing files --- .../opencode/src/cli/cmd/tui/simulation-mcp.ts | 8 ++++++++ .../server/routes/instance/httpapi/simulation.ts | 9 +++++++++ .../opencode/src/testing/simulation/service.ts | 14 +++++++++++++- .../test/testing/simulation/service.test.ts | 11 +++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/simulation-mcp.ts b/packages/opencode/src/cli/cmd/tui/simulation-mcp.ts index b89de021b07..6e433563c6b 100644 --- a/packages/opencode/src/cli/cmd/tui/simulation-mcp.ts +++ b/packages/opencode/src/cli/cmd/tui/simulation-mcp.ts @@ -300,6 +300,14 @@ function createServer(options: Options) { }, async (input) => toolResult(await control(options, "POST", "/experimental/simulation/filesystem/seed", input)), ) + server.registerTool( + "simulation_control_filesystem_write", + { + description: "Write one file into the backend simulated filesystem.", + inputSchema: z.object({ path: z.string(), content: FileContentSchema }), + }, + async (input) => toolResult(await control(options, "POST", "/experimental/simulation/filesystem/write", input)), + ) server.registerTool( "simulation_control_network_register", { diff --git a/packages/opencode/src/server/routes/instance/httpapi/simulation.ts b/packages/opencode/src/server/routes/instance/httpapi/simulation.ts index 01902f059be..b2c1027215b 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/simulation.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/simulation.ts @@ -28,6 +28,15 @@ export const simulationRoute = HttpRouter.use((router) => ), ) + yield* router.add("POST", "/experimental/simulation/filesystem/write", () => + json( + Effect.gen(function* () { + const input = yield* HttpServerRequest.schemaBodyJson(Simulation.FilesystemWriteInput) + return yield* simulation.writeFile(input) + }), + ), + ) + yield* router.add("POST", "/experimental/simulation/network/register", () => json( Effect.gen(function* () { diff --git a/packages/opencode/src/testing/simulation/service.ts b/packages/opencode/src/testing/simulation/service.ts index 36e90e159ff..ce798a8e4c7 100644 --- a/packages/opencode/src/testing/simulation/service.ts +++ b/packages/opencode/src/testing/simulation/service.ts @@ -12,6 +12,11 @@ export const FilesystemSeedInput = Schema.Struct({ files: Schema.Record(Schema.String, FileContent), }) +export const FilesystemWriteInput = Schema.Struct({ + path: Schema.String, + content: FileContent, +}) + export const NetworkRegisterInput = Schema.Union([ Schema.Struct({ kind: Schema.Literal("json"), @@ -80,6 +85,7 @@ export class SimulationLLMError extends Schema.TaggedErrorClass Effect.Effect readonly seedFilesystem: (input: typeof FilesystemSeedInput.Type) => Effect.Effect<{ files: string[] }, unknown> + readonly writeFile: (input: typeof FilesystemWriteInput.Type) => Effect.Effect<{ file: string }, unknown> readonly registerNetwork: (input: typeof NetworkRegisterInput.Type) => Effect.Effect<{ registered: string }, unknown> readonly enqueueLLM: (input: typeof LLMEnqueueInput.Type) => Effect.Effect<{ queued: number }> readonly nextLLM: () => Effect.Effect @@ -131,6 +137,12 @@ export const layer = Layer.effect( return { files } }) + const writeFile = Effect.fn("Simulation.writeFile")(function* (input: typeof FilesystemWriteInput.Type) { + yield* fs.writeWithDirs(path.isAbsolute(input.path) ? input.path : path.join("/opencode", input.path), fileContent(input.content)) + yield* Ref.update(state, (current) => ({ ...current, files: [...current.files, input.path] })) + return { file: input.path } + }) + const registerNetwork = Effect.fn("Simulation.registerNetwork")(function* (input: typeof NetworkRegisterInput.Type) { switch (input.kind) { case "json": @@ -178,7 +190,7 @@ export const layer = Layer.effect( } }) - return Service.of({ reset, seedFilesystem, registerNetwork, enqueueLLM, nextLLM, snapshot }) + return Service.of({ reset, seedFilesystem, writeFile, registerNetwork, enqueueLLM, nextLLM, snapshot }) }), ) diff --git a/packages/opencode/test/testing/simulation/service.test.ts b/packages/opencode/test/testing/simulation/service.test.ts index f099cc687f4..ceb1cad01b1 100644 --- a/packages/opencode/test/testing/simulation/service.test.ts +++ b/packages/opencode/test/testing/simulation/service.test.ts @@ -28,6 +28,17 @@ describe("Simulation", () => { }), ) + it.effect("writes one file into the simulated filesystem", () => + Effect.gen(function* () { + const simulation = yield* Simulation.Service + const fs = yield* AppFileSystem.Service + + expect(yield* simulation.writeFile({ path: "src/generated.txt", content: "generated" })).toEqual({ file: "src/generated.txt" }) + expect(yield* fs.readFileString("/opencode/src/generated.txt")).toBe("generated") + expect((yield* simulation.snapshot()).files).toContain("src/generated.txt") + }), + ) + it.effect("registers network responses through control state", () => Effect.gen(function* () { const simulation = yield* Simulation.Service