diff --git a/packages/opencode/src/reference/repository-cache.ts b/packages/opencode/src/reference/repository-cache.ts index a09b6468bd..5b6d6572e8 100644 --- a/packages/opencode/src/reference/repository-cache.ts +++ b/packages/opencode/src/reference/repository-cache.ts @@ -311,9 +311,4 @@ export const defaultLayer: Layer.Layer = layer.pipe( Layer.provide(Git.defaultLayer), ) -export const ensure = Effect.fn("RepositoryCache.ensure")(function* (input: EnsureInput) { - const cache = yield* Service - return yield* cache.ensure(input) -}) - export * as RepositoryCache from "./repository-cache" diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 1dd972a36c..e5b890a7d2 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -61,6 +61,7 @@ import * as DateTime from "effect/DateTime" import { eq } from "@/storage/db" import * as Database from "@/storage/db" import { SessionTable } from "./session.sql" +import { referencePromptMetadata, referenceTextPart } from "./prompt/reference" import { SessionReminders } from "./reminders" // @ts-ignore @@ -82,88 +83,6 @@ const STRUCTURED_OUTPUT_SYSTEM_PROMPT = `IMPORTANT: The user has requested struc const log = Log.create({ service: "session.prompt" }) const elog = EffectLogger.create({ service: "session.prompt" }) -type ReferencePromptMetadata = { - name: string - kind: "local" | "git" | "invalid" - path?: string - repository?: string - branch?: string - target?: string - targetPath?: string - problem?: string - source: { value: string; start: number; end: number } -} - -function stringField(record: Record, key: string) { - return typeof record[key] === "string" ? record[key] : undefined -} - -function referencePromptMetadata(input: unknown): ReferencePromptMetadata | undefined { - if (!input || typeof input !== "object" || Array.isArray(input)) return - const record = input as Record - const name = stringField(record, "name") - const kind = stringField(record, "kind") - if (!name || (kind !== "local" && kind !== "git" && kind !== "invalid")) return - if (!record.source || typeof record.source !== "object" || Array.isArray(record.source)) return - const source = record.source as Record - const value = stringField(source, "value") - if (!value || typeof source.start !== "number" || typeof source.end !== "number") return - return { - name, - kind, - path: stringField(record, "path"), - repository: stringField(record, "repository"), - branch: stringField(record, "branch"), - target: stringField(record, "target"), - targetPath: stringField(record, "targetPath"), - problem: stringField(record, "problem"), - source: { value, start: source.start, end: source.end }, - } -} - -function referenceTextPart(input: { - reference: Reference.Resolved - source: ReferencePromptMetadata["source"] - target?: string - targetPath?: string - problem?: string -}): MessageV2.TextPartInput { - const metadata: ReferencePromptMetadata = { - name: input.reference.name, - kind: input.reference.kind, - ...(input.reference.kind === "invalid" - ? { repository: input.reference.repository } - : { path: input.reference.path }), - ...(input.reference.kind === "git" - ? { repository: input.reference.repository, branch: input.reference.branch } - : {}), - ...(input.target === undefined ? {} : { target: input.target }), - ...(input.targetPath ? { targetPath: input.targetPath } : {}), - problem: input.problem ?? (input.reference.kind === "invalid" ? input.reference.message : undefined), - source: input.source, - } - const label = metadata.target === undefined ? `@${metadata.name}` : `@${metadata.name}/${metadata.target}` - return { - type: "text", - synthetic: true, - text: [ - `Referenced configured reference ${label}.`, - ...(metadata.kind === "local" ? ["Kind: local directory"] : []), - ...(metadata.kind === "git" ? ["Kind: git repository"] : []), - ...(metadata.repository ? [`Repository: ${metadata.repository}`] : []), - ...(metadata.branch ? [`Branch/ref: ${metadata.branch}`] : []), - ...(metadata.path ? [`Reference root: ${metadata.path}`] : []), - ...(metadata.targetPath ? [`Resolved path: ${metadata.targetPath}`] : []), - ...(metadata.problem - ? [`Problem: ${metadata.problem}`] - : [ - "For targeted context, inspect the reference path directly with Read, Glob, and Grep. For broader research, call the task tool with subagent scout and include this reference path.", - ]), - ].join("\n"), - metadata: { reference: metadata }, - } -} - export interface Interface { readonly cancel: (sessionID: SessionID) => Effect.Effect readonly prompt: (input: PromptInput) => Effect.Effect diff --git a/packages/opencode/src/session/prompt/reference.ts b/packages/opencode/src/session/prompt/reference.ts new file mode 100644 index 0000000000..35321630f2 --- /dev/null +++ b/packages/opencode/src/session/prompt/reference.ts @@ -0,0 +1,69 @@ +import { Option, Schema } from "effect" +import { MessageV2 } from "../message-v2" +import { Reference } from "@/reference/reference" + +const Source = Schema.Struct({ + value: Schema.String, + start: Schema.Number, + end: Schema.Number, +}) + +export const ReferencePromptMetadata = Schema.Struct({ + name: Schema.String, + kind: Schema.Literals(["local", "git", "invalid"]), + path: Schema.optional(Schema.String), + repository: Schema.optional(Schema.String), + branch: Schema.optional(Schema.String), + target: Schema.optional(Schema.String), + targetPath: Schema.optional(Schema.String), + problem: Schema.optional(Schema.String), + source: Source, +}) +export type ReferencePromptMetadata = typeof ReferencePromptMetadata.Type + +const decodeReferencePromptMetadata = Schema.decodeUnknownOption(ReferencePromptMetadata) + +export function referencePromptMetadata(input: unknown) { + return Option.getOrUndefined(decodeReferencePromptMetadata(input)) +} + +export function referenceTextPart(input: { + reference: Reference.Resolved + source: ReferencePromptMetadata["source"] + target?: string + targetPath?: string + problem?: string +}): MessageV2.TextPartInput { + const metadata: ReferencePromptMetadata = { + name: input.reference.name, + kind: input.reference.kind, + ...(input.reference.kind === "invalid" ? { repository: input.reference.repository } : { path: input.reference.path }), + ...(input.reference.kind === "git" ? { repository: input.reference.repository, branch: input.reference.branch } : {}), + ...(input.target === undefined ? {} : { target: input.target }), + ...(input.targetPath ? { targetPath: input.targetPath } : {}), + problem: input.problem ?? (input.reference.kind === "invalid" ? input.reference.message : undefined), + source: input.source, + } + const label = metadata.target === undefined ? `@${metadata.name}` : `@${metadata.name}/${metadata.target}` + return { + type: "text", + synthetic: true, + text: [ + `Referenced configured reference ${label}.`, + ...(metadata.kind === "local" ? ["Kind: local directory"] : []), + ...(metadata.kind === "git" ? ["Kind: git repository"] : []), + ...(metadata.repository ? [`Repository: ${metadata.repository}`] : []), + ...(metadata.branch ? [`Branch/ref: ${metadata.branch}`] : []), + ...(metadata.path ? [`Reference root: ${metadata.path}`] : []), + ...(metadata.targetPath ? [`Resolved path: ${metadata.targetPath}`] : []), + ...(metadata.problem + ? [`Problem: ${metadata.problem}`] + : [ + "For targeted context, inspect the reference path directly with Read, Glob, and Grep. For broader research, call the task tool with subagent scout and include this reference path.", + ]), + ].join("\n"), + metadata: { reference: metadata }, + } +} + +export * as ReferencePrompt from "./reference"