|
|
@@ -2,6 +2,8 @@ import { describe, expect } from "bun:test"
|
|
|
import { Effect, Layer } from "effect"
|
|
|
import fs from "fs/promises"
|
|
|
import path from "path"
|
|
|
+import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
|
|
+import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
|
|
import { FSUtil } from "@opencode-ai/core/fs-util"
|
|
|
import { Global } from "@opencode-ai/core/global"
|
|
|
import { InstructionContext } from "@opencode-ai/core/instruction-context"
|
|
|
@@ -15,6 +17,15 @@ import { testEffect } from "./lib/effect"
|
|
|
|
|
|
const it = testEffect(Layer.empty)
|
|
|
|
|
|
+const instructionLayer = (
|
|
|
+ input: { config: string; locationServiceLayer: Layer.Layer<Location.Service>; filesystemLayer?: Layer.Layer<FSUtil.Service> },
|
|
|
+) =>
|
|
|
+ AppNodeBuilder.build(LayerNode.group([SystemContextRegistry.node, InstructionContext.node]), [
|
|
|
+ [Global.node, Global.layerWith({ config: input.config })],
|
|
|
+ [Location.node, input.locationServiceLayer],
|
|
|
+ ...(input.filesystemLayer ? [[FSUtil.node, input.filesystemLayer] as const] : []),
|
|
|
+ ])
|
|
|
+
|
|
|
describe("InstructionContext", () => {
|
|
|
it.live("loads global and upward project AGENTS.md files as one aggregate context", () =>
|
|
|
Effect.acquireRelease(
|
|
|
@@ -41,19 +52,19 @@ describe("InstructionContext", () => {
|
|
|
|
|
|
const load = SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
- Effect.provide(FSUtil.defaultLayer),
|
|
|
- Effect.provide(Global.layerWith({ config: global })),
|
|
|
Effect.provide(
|
|
|
- Layer.succeed(
|
|
|
- Location.Service,
|
|
|
- Location.Service.of(
|
|
|
- location(
|
|
|
- { directory: AbsolutePath.make(directory) },
|
|
|
- { projectDirectory: AbsolutePath.make(project) },
|
|
|
+ instructionLayer({
|
|
|
+ config: global,
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(
|
|
|
+ location(
|
|
|
+ { directory: AbsolutePath.make(directory) },
|
|
|
+ { projectDirectory: AbsolutePath.make(project) },
|
|
|
+ ),
|
|
|
),
|
|
|
),
|
|
|
- ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|
|
|
@@ -107,14 +118,14 @@ describe("InstructionContext", () => {
|
|
|
yield* Effect.promise(() => fs.writeFile(file, ""))
|
|
|
const context = yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
- Effect.provide(FSUtil.defaultLayer),
|
|
|
- Effect.provide(Global.layerWith({ config: path.join(tmp.path, "global") })),
|
|
|
Effect.provide(
|
|
|
- Layer.succeed(
|
|
|
- Location.Service,
|
|
|
- Location.Service.of(location({ directory: AbsolutePath.make(tmp.path) })),
|
|
|
- ),
|
|
|
+ instructionLayer({
|
|
|
+ config: path.join(tmp.path, "global"),
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(location({ directory: AbsolutePath.make(tmp.path) })),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|
|
|
@@ -133,14 +144,18 @@ describe("InstructionContext", () => {
|
|
|
FSUtil.Service.of({ ...fs, up: () => Effect.fail(new FSUtil.FileSystemError({ method: "up" })) }),
|
|
|
),
|
|
|
),
|
|
|
- ).pipe(Layer.provide(FSUtil.defaultLayer))
|
|
|
+ ).pipe(Layer.provide(LayerNode.compile(FSUtil.node)))
|
|
|
const context = yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
- Effect.provide(failingFS),
|
|
|
- Effect.provide(Global.layerWith({ config: "/global" })),
|
|
|
Effect.provide(
|
|
|
- Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("/repo") }))),
|
|
|
+ instructionLayer({
|
|
|
+ config: "/global",
|
|
|
+ filesystemLayer: failingFS,
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(location({ directory: AbsolutePath.make("/repo") })),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|
|
|
@@ -169,14 +184,18 @@ describe("InstructionContext", () => {
|
|
|
}),
|
|
|
),
|
|
|
),
|
|
|
- ).pipe(Layer.provide(FSUtil.defaultLayer))
|
|
|
+ ).pipe(Layer.provide(LayerNode.compile(FSUtil.node)))
|
|
|
const context = yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
- Effect.provide(racingFS),
|
|
|
- Effect.provide(Global.layerWith({ config: "/global" })),
|
|
|
Effect.provide(
|
|
|
- Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("/repo") }))),
|
|
|
+ instructionLayer({
|
|
|
+ config: "/global",
|
|
|
+ filesystemLayer: racingFS,
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(location({ directory: AbsolutePath.make("/repo") })),
|
|
|
+ ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|
|
|
@@ -208,20 +227,21 @@ describe("InstructionContext", () => {
|
|
|
}),
|
|
|
),
|
|
|
),
|
|
|
- ).pipe(Layer.provide(FSUtil.defaultLayer))
|
|
|
+ ).pipe(Layer.provide(LayerNode.compile(FSUtil.node)))
|
|
|
|
|
|
yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
- Effect.provide(observingFS),
|
|
|
- Effect.provide(Global.layerWith({ config: "/global" })),
|
|
|
Effect.provide(
|
|
|
- Layer.succeed(
|
|
|
- Location.Service,
|
|
|
- Location.Service.of(
|
|
|
- location({ directory: AbsolutePath.make("/repo/") }, { projectDirectory: AbsolutePath.make("/repo") }),
|
|
|
+ instructionLayer({
|
|
|
+ config: "/global",
|
|
|
+ filesystemLayer: observingFS,
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(
|
|
|
+ location({ directory: AbsolutePath.make("/repo/") }, { projectDirectory: AbsolutePath.make("/repo") }),
|
|
|
+ ),
|
|
|
),
|
|
|
- ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|
|
|
@@ -241,18 +261,20 @@ describe("InstructionContext", () => {
|
|
|
|
|
|
yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
Effect.provide(
|
|
|
- Layer.effect(
|
|
|
- FSUtil.Service,
|
|
|
- FSUtil.Service.pipe(
|
|
|
- Effect.map((fs) => FSUtil.Service.of({ ...fs, up: () => Effect.sync(() => ((scanned = true), [])) })),
|
|
|
+ instructionLayer({
|
|
|
+ config: "/global",
|
|
|
+ filesystemLayer: Layer.effect(
|
|
|
+ FSUtil.Service,
|
|
|
+ FSUtil.Service.pipe(
|
|
|
+ Effect.map((fs) => FSUtil.Service.of({ ...fs, up: () => Effect.sync(() => ((scanned = true), [])) })),
|
|
|
+ ),
|
|
|
+ ).pipe(Layer.provide(LayerNode.compile(FSUtil.node))),
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(location({ directory: AbsolutePath.make("/repo") })),
|
|
|
),
|
|
|
- ).pipe(Layer.provide(FSUtil.defaultLayer)),
|
|
|
- ),
|
|
|
- Effect.provide(Global.layerWith({ config: "/global" })),
|
|
|
- Effect.provide(
|
|
|
- Layer.succeed(Location.Service, Location.Service.of(location({ directory: AbsolutePath.make("/repo") }))),
|
|
|
+ }),
|
|
|
),
|
|
|
Effect.ensuring(
|
|
|
Effect.sync(() => {
|
|
|
@@ -271,23 +293,22 @@ describe("InstructionContext", () => {
|
|
|
let scanned = false
|
|
|
yield* SystemContextRegistry.Service.pipe(
|
|
|
Effect.flatMap((service) => service.load()),
|
|
|
- Effect.provide(InstructionContext.layer.pipe(Layer.provideMerge(SystemContextRegistry.layer))),
|
|
|
Effect.provide(
|
|
|
- Layer.effect(
|
|
|
- FSUtil.Service,
|
|
|
- FSUtil.Service.pipe(
|
|
|
- Effect.map((fs) => FSUtil.Service.of({ ...fs, up: () => Effect.sync(() => ((scanned = true), [])) })),
|
|
|
- ),
|
|
|
- ).pipe(Layer.provide(FSUtil.defaultLayer)),
|
|
|
- ),
|
|
|
- Effect.provide(Global.layerWith({ config: "/global" })),
|
|
|
- Effect.provide(
|
|
|
- Layer.succeed(
|
|
|
- Location.Service,
|
|
|
- Location.Service.of(
|
|
|
- location({ directory: AbsolutePath.make("/outside") }, { projectDirectory: AbsolutePath.make("/repo") }),
|
|
|
+ instructionLayer({
|
|
|
+ config: "/global",
|
|
|
+ filesystemLayer: Layer.effect(
|
|
|
+ FSUtil.Service,
|
|
|
+ FSUtil.Service.pipe(
|
|
|
+ Effect.map((fs) => FSUtil.Service.of({ ...fs, up: () => Effect.sync(() => ((scanned = true), [])) })),
|
|
|
+ ),
|
|
|
+ ).pipe(Layer.provide(LayerNode.compile(FSUtil.node))),
|
|
|
+ locationServiceLayer: Layer.succeed(
|
|
|
+ Location.Service,
|
|
|
+ Location.Service.of(
|
|
|
+ location({ directory: AbsolutePath.make("/outside") }, { projectDirectory: AbsolutePath.make("/repo") }),
|
|
|
+ ),
|
|
|
),
|
|
|
- ),
|
|
|
+ }),
|
|
|
),
|
|
|
)
|
|
|
|