import { describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { AbsolutePath } from "@opencode-ai/core/schema" import { Reference } from "@opencode-ai/core/reference" import { ReferenceGuidance } from "@opencode-ai/core/reference/guidance" import { Instructions } from "@opencode-ai/core/instructions/index" import { it } from "./lib/effect" const guidanceLayer = (referenceLayer: Layer.Layer) => AppNodeBuilder.build(ReferenceGuidance.node, [[Reference.node, referenceLayer]]) describe("ReferenceGuidance", () => { it.effect("lists available references in the instructions", () => Effect.gen(function* () { const guidance = yield* ReferenceGuidance.Service const generation = yield* Instructions.initialize(yield* guidance.load()) expect(generation.text).toContain("") expect(generation.text).toContain("docs") expect(generation.text).toContain("/docs") expect(generation.text).toContain("Use for product documentation") }).pipe( Effect.provide( guidanceLayer( Layer.mock(Reference.Service, { list: () => Effect.succeed([ new Reference.Info({ name: "docs", path: AbsolutePath.make("/docs"), description: "Use for product documentation", source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make("/docs"), description: "Use for product documentation", }), }), ]), }), ), ), ), ) it.effect("omits guidance when no references are available", () => Effect.gen(function* () { const guidance = yield* ReferenceGuidance.Service const generation = yield* Instructions.initialize(yield* guidance.load()) expect(generation.text).toBe("") }).pipe(Effect.provide(guidanceLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed([]) })))), ) it.effect("omits references without descriptions", () => Effect.gen(function* () { const guidance = yield* ReferenceGuidance.Service const generation = yield* Instructions.initialize(yield* guidance.load()) expect(generation.text).toBe("") }).pipe( Effect.provide( guidanceLayer( Layer.mock(Reference.Service, { list: () => Effect.succeed([ new Reference.Info({ name: "docs", path: AbsolutePath.make("/docs"), source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make("/docs") }), }), ]), }), ), ), ), ) it.effect("announces added and removed references as deltas", () => { const reference = (name: string, description: string) => new Reference.Info({ name, path: AbsolutePath.make(`/${name}`), description, source: Reference.LocalSource.make({ type: "local", path: AbsolutePath.make(`/${name}`), description }), }) let references = [reference("docs", "Use for product documentation")] return Effect.gen(function* () { const guidance = yield* ReferenceGuidance.Service const initialized = yield* Instructions.initialize(yield* guidance.load()) references = [reference("docs", "Use for product documentation"), reference("examples", "Use for examples")] const added = yield* Instructions.reconcile(yield* guidance.load(), initialized.applied) expect(added).toMatchObject({ _tag: "Updated", text: [ "New project references are available in addition to those previously listed:", " ", " examples", " /examples", " Use for examples", " ", ].join("\n"), }) references = [reference("examples", "Use for examples")] expect( yield* Instructions.reconcile(yield* guidance.load(), added._tag === "Updated" ? added.applied : {}), ).toMatchObject({ _tag: "Updated", text: "The following project references are no longer available and must not be used: docs.", }) }).pipe(Effect.provide(guidanceLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed(references) })))) }) })