diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index 81f2251a..457890d5 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -1,7 +1,7 @@ import { z } from "zod" import { Bus } from "../bus" import { $ } from "bun" -import { createPatch } from "diff" +import { formatPatch, structuredPatch } from "diff" import path from "path" import fs from "fs" import ignore from "ignore" @@ -28,6 +28,7 @@ export namespace File { .object({ name: z.string(), path: z.string(), + absolute: z.string(), type: z.enum(["file", "directory"]), ignored: z.boolean(), }) @@ -36,6 +37,34 @@ export namespace File { }) export type Node = z.infer + export const Content = z + .object({ + content: z.string(), + diff: z.string().optional(), + patch: z + .object({ + oldFileName: z.string(), + newFileName: z.string(), + oldHeader: z.string().optional(), + newHeader: z.string().optional(), + hunks: z.array( + z.object({ + oldStart: z.number(), + oldLines: z.number(), + newStart: z.number(), + newLines: z.number(), + lines: z.array(z.string()), + }), + ), + index: z.string().optional(), + }) + .optional(), + }) + .openapi({ + ref: "FileContent", + }) + export type Content = z.infer + export const Event = { Edited: Bus.event( "file.edited", @@ -127,13 +156,14 @@ export namespace File { const diff = await $`git diff ${file}`.cwd(Instance.directory).quiet().nothrow().text() if (diff.trim()) { const original = await $`git show HEAD:${file}`.cwd(Instance.directory).quiet().nothrow().text() - const patch = createPatch(file, original, content, "old", "new", { + const diff = structuredPatch(file, file, original, content, "old", "new", { context: Infinity, }) - return { type: "patch", content: patch } + const patch = formatPatch(diff) + return { content, patch, diff } } } - return { type: "raw", content } + return { content } } export async function list(dir?: string) { @@ -157,6 +187,7 @@ export namespace File { nodes.push({ name: entry.name, path: relativePath, + absolute: fullPath, type, ignored: ignored(type === "directory" ? relativePath + "/" : relativePath), }) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 01ade6d1..9091f050 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -974,12 +974,7 @@ export namespace Server { description: "File content", content: { "application/json": { - schema: resolver( - z.object({ - type: z.enum(["raw", "patch"]), - content: z.string(), - }), - ), + schema: resolver(File.Content), }, }, }, diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 10e06acd..1bee5442 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -527,10 +527,10 @@ export namespace Session { break } } - offset = Math.max(start - 2, 0) - if (end) { - limit = end - offset + 2 - } + } + offset = Math.max(start - 1, 0) + if (end) { + limit = end - offset } } const args = { filePath, offset, limit } diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index c56ea309..8befce1c 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1117,10 +1117,30 @@ export type Symbol = { export type FileNode = { name: string path: string + absolute: string type: "file" | "directory" ignored: boolean } +export type FileContent = { + content: string + diff?: string + patch?: { + oldFileName: string + newFileName: string + oldHeader?: string + newHeader?: string + hunks: Array<{ + oldStart: number + oldLines: number + newStart: number + newLines: number + lines: Array + }> + index?: string + } +} + export type File = { path: string added: number @@ -1893,10 +1913,7 @@ export type FileReadResponses = { /** * File content */ - 200: { - type: "raw" | "patch" - content: string - } + 200: FileContent } export type FileReadResponse = FileReadResponses[keyof FileReadResponses]