From 30edf5aa42cab93f6fe392af58a285be6b647ff4 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 10 Apr 2026 23:40:14 -0400 Subject: [PATCH] refactor(session): convert SystemPrompt facade calls to effectful versions Replace `Effect.promise(() => SystemPrompt.skills(...))` and `Effect.promise(() => SystemPrompt.environment(...))` with direct effectful calls. `environment` is now a plain sync function (the async Ripgrep.tree call was dead code behind `&& false`). `skills` is now an `Effect.fn` that takes the Skill service as a parameter. --- packages/opencode/src/session/prompt.ts | 10 ++++---- packages/opencode/src/session/system.ts | 23 +++++++------------ .../test/session/prompt-effect.test.ts | 1 + packages/opencode/test/session/system.test.ts | 11 +++++++-- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index de26e03286..0f98729f68 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -48,6 +48,7 @@ import { EffectLogger } from "@/effect/logger" import { InstanceState } from "@/effect/instance-state" import { makeRuntime } from "@/effect/run-service" import { TaskTool, type TaskPromptOps } from "@/tool/task" +import { Skill } from "@/skill" import { SessionRunState } from "./run-state" // @ts-ignore @@ -102,6 +103,7 @@ export namespace SessionPrompt { const instruction = yield* Instruction.Service const state = yield* SessionRunState.Service const revert = yield* SessionRevert.Service + const skill = yield* Skill.Service const run = { promise: (effect: Effect.Effect) => @@ -1469,8 +1471,8 @@ NOTE: At any point in time through this workflow you should feel free to ask the yield* plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs }) const [skills, env, instructions, modelMsgs] = yield* Effect.all([ - Effect.promise(() => SystemPrompt.skills(agent)), - Effect.promise(() => SystemPrompt.environment(model)), + SystemPrompt.skills(agent, skill), + Effect.sync(() => SystemPrompt.environment(model)), instruction.system().pipe(Effect.orDie), MessageV2.toModelMessagesEffect(msgs, model), ]) @@ -1694,9 +1696,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the Layer.provide(Plugin.defaultLayer), Layer.provide(Session.defaultLayer), Layer.provide(SessionRevert.defaultLayer), - Layer.provide(Agent.defaultLayer), - Layer.provide(Bus.layer), - Layer.provide(CrossSpawnSpawner.defaultLayer), + Layer.provide(Layer.mergeAll(Agent.defaultLayer, Skill.defaultLayer, Bus.layer, CrossSpawnSpawner.defaultLayer)), ), ) const { runPromise } = makeRuntime(Service, defaultLayer) diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 09788f3cdb..e492d1d558 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -1,4 +1,4 @@ -import { Ripgrep } from "../file/ripgrep" +import { Context, Effect } from "effect" import { Instance } from "../project/instance" @@ -33,7 +33,7 @@ export namespace SystemPrompt { return [PROMPT_DEFAULT] } - export async function environment(model: Provider.Model) { + export function environment(model: Provider.Model) { const project = Instance.project return [ [ @@ -46,24 +46,17 @@ export namespace SystemPrompt { ` Platform: ${process.platform}`, ` Today's date: ${new Date().toDateString()}`, ``, - ``, - ` ${ - project.vcs === "git" && false - ? await Ripgrep.tree({ - cwd: Instance.directory, - limit: 50, - }) - : "" - }`, - ``, ].join("\n"), ] } - export async function skills(agent: Agent.Info) { + export const skills = Effect.fn("SystemPrompt.skills")(function* ( + agent: Agent.Info, + skill: Context.Service.Shape, + ) { if (Permission.disabled(["skill"], agent.permission).has("skill")) return - const list = await Skill.available(agent) + const list = yield* skill.available(agent) return [ "Skills provide specialized instructions and workflows for specific tasks.", @@ -72,5 +65,5 @@ export namespace SystemPrompt { // version of them here and a less verbose version in tool description, rather than vice versa. Skill.fmt(list, { verbose: true }), ].join("\n") - } + }) } diff --git a/packages/opencode/test/session/prompt-effect.test.ts b/packages/opencode/test/session/prompt-effect.test.ts index ba33cb086e..300b4b408a 100644 --- a/packages/opencode/test/session/prompt-effect.test.ts +++ b/packages/opencode/test/session/prompt-effect.test.ts @@ -193,6 +193,7 @@ function makeHttp() { Layer.provideMerge(registry), Layer.provideMerge(trunc), Layer.provide(Instruction.defaultLayer), + Layer.provide(Skill.defaultLayer), Layer.provideMerge(deps), ), ) diff --git a/packages/opencode/test/session/system.test.ts b/packages/opencode/test/session/system.test.ts index 47f5f6fc25..fdda6dde1f 100644 --- a/packages/opencode/test/session/system.test.ts +++ b/packages/opencode/test/session/system.test.ts @@ -1,7 +1,9 @@ import { describe, expect, test } from "bun:test" import path from "path" +import { Effect } from "effect" import { Agent } from "../../src/agent/agent" import { Instance } from "../../src/project/instance" +import { Skill } from "../../src/skill" import { SystemPrompt } from "../../src/session/system" import { tmpdir } from "../fixture/fixture" @@ -38,8 +40,13 @@ description: ${description} directory: tmp.path, fn: async () => { const build = await Agent.get("build") - const first = await SystemPrompt.skills(build!) - const second = await SystemPrompt.skills(build!) + const runSkills = Effect.gen(function* () { + const svc = yield* Skill.Service + return yield* SystemPrompt.skills(build!, svc) + }).pipe(Effect.provide(Skill.defaultLayer)) + + const first = await Effect.runPromise(runSkills) + const second = await Effect.runPromise(runSkills) expect(first).toBe(second)