| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- export * as InstructionContext from "./instruction-context"
- import { Array, Effect, Layer, Schema } from "effect"
- import { isAbsolute, join, relative, sep } from "path"
- import { FSUtil } from "./fs-util"
- import { Flag } from "./flag/flag"
- import { Global } from "./global"
- import { Location } from "./location"
- import { AbsolutePath } from "./schema"
- import { SystemContext } from "./system-context/index"
- import { SystemContextRegistry } from "./system-context/registry"
- import { makeLocationNode } from "./effect/app-node"
- class File extends Schema.Class<File>("InstructionContext.File")({
- path: AbsolutePath,
- content: Schema.String,
- }) {}
- const Files = Schema.Array(File)
- const key = SystemContext.Key.make("core/instructions")
- const layer = Layer.effectDiscard(
- Effect.gen(function* () {
- const fs = yield* FSUtil.Service
- const global = yield* Global.Service
- const location = yield* Location.Service
- const registry = yield* SystemContextRegistry.Service
- const source = (value: ReadonlyArray<File> | SystemContext.Unavailable) =>
- SystemContext.make({
- key,
- codec: Schema.toCodecJson(Files),
- load: Effect.succeed(value),
- baseline: render,
- update: (_previous, current) =>
- `These instructions replace all previously loaded ambient instructions.\n\n${render(current)}`,
- removed: () => "Previously loaded instructions no longer apply.",
- })
- const observe = Effect.fn("InstructionContext.observe")(function* () {
- const start = yield* fs.resolve(location.directory)
- const stop = yield* fs.resolve(location.project.directory)
- const fromProject = relative(stop, start)
- const insideProject =
- fromProject === "" || (fromProject !== ".." && !fromProject.startsWith(`..${sep}`) && !isAbsolute(fromProject))
- const discovered = new Set(
- yield* Effect.forEach(
- Flag.KIRINCODE_DISABLE_PROJECT_CONFIG || !insideProject
- ? []
- : yield* fs.up({
- targets: ["AGENTS.md"],
- start,
- stop,
- }),
- fs.resolve,
- ),
- )
- const paths = Array.dedupe([yield* fs.resolve(join(global.config, "AGENTS.md")), ...discovered])
- const files = yield* Effect.forEach(
- paths,
- (path) =>
- fs
- .readFileStringSafe(path)
- .pipe(
- Effect.map((content) =>
- content === undefined ? undefined : new File({ path: AbsolutePath.make(path), content }),
- ),
- ),
- { concurrency: "unbounded" },
- )
- if (files.some((file, index) => file === undefined && discovered.has(paths[index])))
- return SystemContext.unavailable
- return files.filter((file): file is File => file !== undefined)
- })
- yield* registry.register({
- key,
- load: observe().pipe(
- Effect.map((files) =>
- files === SystemContext.unavailable
- ? source(files)
- : files.length === 0
- ? SystemContext.empty
- : source(files),
- ),
- Effect.catch(() => Effect.succeed(source(SystemContext.unavailable))),
- Effect.catchDefect(() => Effect.succeed(source(SystemContext.unavailable))),
- ),
- })
- }),
- )
- export const node = makeLocationNode({
- name: "instruction-context",
- layer,
- deps: [FSUtil.node, Global.node, Location.node, SystemContextRegistry.node],
- })
- function render(files: ReadonlyArray<File>) {
- return files.map((file) => `Instructions from: ${file.path}\n${file.content}`).join("\n\n")
- }
|