diff --git a/STATS.md b/STATS.md index d034d2419..39b4056b6 100644 --- a/STATS.md +++ b/STATS.md @@ -117,3 +117,4 @@ | 2025-10-21 | 548,721 (+7,457) | 479,703 (+6,751) | 1,028,424 (+14,208) | | 2025-10-22 | 557,949 (+9,228) | 491,395 (+11,692) | 1,049,344 (+20,920) | | 2025-10-23 | 564,716 (+6,767) | 498,736 (+7,341) | 1,063,452 (+14,108) | +| 2025-10-24 | 572,692 (+7,976) | 506,905 (+8,169) | 1,079,597 (+16,145) | diff --git a/bun.lock b/bun.lock index 790de474b..24ae8b254 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,11 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -66,7 +70,11 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -90,7 +98,11 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -111,7 +123,11 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -150,7 +166,11 @@ }, "packages/function": { "name": "@opencode-ai/function", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "22.0.0", @@ -166,7 +186,11 @@ }, "packages/opencode": { "name": "opencode", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "bin": { "opencode": "./bin/opencode", }, @@ -241,7 +265,11 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -261,7 +289,11 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "devDependencies": { "@hey-api/openapi-ts": "0.81.0", "@tsconfig/node22": "catalog:", @@ -272,7 +304,11 @@ }, "packages/slack": { "name": "@opencode-ai/slack", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -285,7 +321,11 @@ }, "packages/ui": { "name": "@opencode-ai/ui", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@kobalte/core": "catalog:", "@pierre/precision-diffs": "0.0.2-alpha.1-1", @@ -308,7 +348,11 @@ }, "packages/web": { "name": "@opencode-ai/web", +<<<<<<< HEAD "version": "0.15.15", +======= + "version": "0.15.16", +>>>>>>> dev "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index dad9e056f..29daed872 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -7,7 +7,7 @@ "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "start": "vinxi start", - "version": "0.15.15" + "version": "0.15.16" }, "dependencies": { "@ibm/plex": "6.4.1", diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ec0a2b861..e429f769b 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "0.15.15", + "version": "0.15.16", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/function/package.json b/packages/console/function/package.json index eeac1e688..f9d9ee4b0 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "0.15.15", + "version": "0.15.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 191c9ea4c..05048f6b0 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "0.15.15", + "version": "0.15.16", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 92bf33eb5..48c3aaced 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/desktop", - "version": "0.15.15", + "version": "0.15.16", "description": "", "type": "module", "scripts": { diff --git a/packages/function/package.json b/packages/function/package.json index 83e17c9e8..1d7da00ff 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "0.15.15", + "version": "0.15.16", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 265ca9abc..693f9d162 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "0.15.15", + "version": "0.15.16", "name": "opencode", "type": "module", "private": true, diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts index 427260ba4..b936f54e4 100644 --- a/packages/opencode/src/cli/cmd/tui.ts +++ b/packages/opencode/src/cli/cmd/tui.ts @@ -157,7 +157,7 @@ export const TuiCommand = cmd({ ;(async () => { // if (Installation.isLocal()) return - const config = await Config.global() + const config = await Config.get() if (config.autoupdate === false || Flag.OPENCODE_DISABLE_AUTOUPDATE) return const latest = await Installation.latest().catch(() => {}) if (!latest) return diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 36d124018..f286333e3 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -838,4 +838,126 @@ export namespace LSPServer { } }, } + + export const LuaLS: Info = { + id: "lua-ls", + root: NearestRoot([ + ".luarc.json", + ".luarc.jsonc", + ".luacheckrc", + ".stylua.toml", + "stylua.toml", + "selene.toml", + "selene.yml", + ]), + extensions: [".lua"], + async spawn(root) { + let bin = Bun.which("lua-language-server", { + PATH: process.env["PATH"] + ":" + Global.Path.bin, + }) + + if (!bin) { + if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return + log.info("downloading lua-language-server from GitHub releases") + + const releaseResponse = await fetch("https://api.github.com/repos/LuaLS/lua-language-server/releases/latest") + if (!releaseResponse.ok) { + log.error("Failed to fetch lua-language-server release info") + return + } + + const release = await releaseResponse.json() + + const platform = process.platform + const arch = process.arch + let assetName = "" + + let lualsArch: string = arch + if (arch === "arm64") lualsArch = "arm64" + else if (arch === "x64") lualsArch = "x64" + else if (arch === "ia32") lualsArch = "ia32" + + let lualsPlatform: string = platform + if (platform === "darwin") lualsPlatform = "darwin" + else if (platform === "linux") lualsPlatform = "linux" + else if (platform === "win32") lualsPlatform = "win32" + + const ext = platform === "win32" ? "zip" : "tar.gz" + + assetName = `lua-language-server-${release.tag_name}-${lualsPlatform}-${lualsArch}.${ext}` + + const supportedCombos = [ + "darwin-arm64.tar.gz", + "darwin-x64.tar.gz", + "linux-x64.tar.gz", + "linux-arm64.tar.gz", + "win32-x64.zip", + "win32-ia32.zip", + ] + + const assetSuffix = `${lualsPlatform}-${lualsArch}.${ext}` + if (!supportedCombos.includes(assetSuffix)) { + log.error(`Platform ${platform} and architecture ${arch} is not supported by lua-language-server`) + return + } + + const asset = release.assets.find((a: any) => a.name === assetName) + if (!asset) { + log.error(`Could not find asset ${assetName} in latest lua-language-server release`) + return + } + + const downloadUrl = asset.browser_download_url + const downloadResponse = await fetch(downloadUrl) + if (!downloadResponse.ok) { + log.error("Failed to download lua-language-server") + return + } + + const tempPath = path.join(Global.Path.bin, assetName) + await Bun.file(tempPath).write(downloadResponse) + + // Unlike zls which is a single self-contained binary, + // lua-language-server needs supporting files (meta/, locale/, etc.) + // Extract entire archive to dedicated directory to preserve all files + const installDir = path.join(Global.Path.bin, `lua-language-server-${lualsArch}-${lualsPlatform}`) + + // Remove old installation if exists + const stats = await fs.stat(installDir).catch(() => undefined) + if (stats) { + await fs.rm(installDir, { force: true, recursive: true }) + } + + await fs.mkdir(installDir, { recursive: true }) + + if (ext === "zip") { + await $`unzip -o -q ${tempPath} -d ${installDir}`.quiet().nothrow() + } else { + await $`tar -xzf ${tempPath} -C ${installDir}`.nothrow() + } + + await fs.rm(tempPath, { force: true }) + + // Binary is located in bin/ subdirectory within the extracted archive + bin = path.join(installDir, "bin", "lua-language-server" + (platform === "win32" ? ".exe" : "")) + + if (!(await Bun.file(bin).exists())) { + log.error("Failed to extract lua-language-server binary") + return + } + + if (platform !== "win32") { + await $`chmod +x ${bin}`.nothrow() + } + + log.info(`installed lua-language-server`, { bin }) + } + + return { + process: spawn(bin, { + cwd: root, + }), + } + }, + } } diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 49003b2ba..babaebc80 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -3,7 +3,7 @@ import { Bus } from "../bus" import { describeRoute, generateSpecs, validator, resolver, openAPIRouteHandler } from "hono-openapi" import { Hono } from "hono" import { cors } from "hono/cors" -import { streamSSE } from "hono/streaming" +import { stream, streamSSE } from "hono/streaming" import { Session } from "../session" import z from "zod/v4" import { Provider } from "../provider/provider" @@ -814,10 +814,14 @@ export namespace Server { ), validator("json", SessionPrompt.PromptInput.omit({ sessionID: true })), async (c) => { - const sessionID = c.req.valid("param").id - const body = c.req.valid("json") - const msg = await SessionPrompt.prompt({ ...body, sessionID }) - return c.json(msg) + c.status(200) + c.header("Content-Type", "application/json") + return stream(c, async (stream) => { + const sessionID = c.req.valid("param").id + const body = c.req.valid("json") + const msg = await SessionPrompt.prompt({ ...body, sessionID }) + stream.write(JSON.stringify(msg)) + }) }, ) .post( diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 953c93ff9..674bb9100 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -269,8 +269,9 @@ export namespace MessageV2 { }), summary: z .object({ + title: z.string().optional(), + body: z.string().optional(), diffs: Snapshot.FileDiff.array(), - text: z.string(), }) .optional(), }).meta({ diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 6d7b59e5b..de8567882 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -2,12 +2,15 @@ import { Provider } from "@/provider/provider" import { fn } from "@/util/fn" import z from "zod" import { Session } from "." -import { generateText } from "ai" +import { generateText, type ModelMessage } from "ai" import { MessageV2 } from "./message-v2" import { Flag } from "@/flag/flag" import { Identifier } from "@/id/id" import { Snapshot } from "@/snapshot" +import { ProviderTransform } from "@/provider/transform" +import { SystemPrompt } from "./system" + export namespace SessionSummary { export const summarize = fn( z.object({ @@ -37,19 +40,43 @@ export namespace SessionSummary { const messages = input.messages.filter( (m) => m.info.id === input.messageID || (m.info.role === "assistant" && m.info.parentID === input.messageID), ) - const userMsg = messages.find((m) => m.info.id === input.messageID)! + const msgWithParts = messages.find((m) => m.info.id === input.messageID)! + const userMsg = msgWithParts.info as MessageV2.User const diffs = await computeDiff({ messages }) - userMsg.info.summary = { + userMsg.summary = { + ...userMsg.summary, diffs, - text: "", } - if ( - Flag.OPENCODE_EXPERIMENTAL_TURN_SUMMARY && - messages.every((m) => m.info.role !== "assistant" || m.info.time.completed) - ) { - const assistantMsg = messages.find((m) => m.info.role === "assistant")!.info as MessageV2.Assistant - const small = await Provider.getSmallModel(assistantMsg.providerID) - if (!small) return + await Session.updateMessage(userMsg) + + const assistantMsg = messages.find((m) => m.info.role === "assistant")!.info as MessageV2.Assistant + const small = await Provider.getSmallModel(assistantMsg.providerID) + if (!small) return + + const textPart = msgWithParts.parts.find((p) => p.type === "text" && p.synthetic === false) as MessageV2.TextPart + if (textPart && !userMsg.summary?.title) { + const result = await generateText({ + maxOutputTokens: small.info.reasoning ? 1500 : 20, + providerOptions: ProviderTransform.providerOptions(small.npm, small.providerID, {}), + messages: [ + ...SystemPrompt.title(small.providerID).map( + (x): ModelMessage => ({ + role: "system", + content: x, + }), + ), + { + role: "user" as const, + content: textPart?.text ?? "", + }, + ], + model: small.language, + }) + userMsg.summary.title = result.text + await Session.updateMessage(userMsg) + } + + if (messages.every((m) => m.info.role !== "assistant" || m.info.time.completed)) { const result = await generateText({ model: small.language, maxOutputTokens: 100, @@ -65,12 +92,9 @@ export namespace SessionSummary { }, ], }) - userMsg.info.summary = { - text: result.text, - diffs: [], - } + userMsg.summary.body = result.text + await Session.updateMessage(userMsg) } - await Session.updateMessage(userMsg.info) } export const diff = fn( diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index e54fcd446..9eb0f8f51 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -23,6 +23,7 @@ export namespace Storage { const MIGRATIONS: Migration[] = [ async (dir) => { const project = path.resolve(dir, "../project") + if (!fs.exists(project)) return for await (const projectDir of new Bun.Glob("*").scan({ cwd: project, onlyFiles: false, @@ -177,8 +178,7 @@ export namespace Storage { async function withErrorHandling(body: () => Promise) { return body().catch((e) => { - if (!(e instanceof Error)) - throw e + if (!(e instanceof Error)) throw e const errnoException = e as NodeJS.ErrnoException if (errnoException.code === "ENOENT") { throw new NotFoundError({ message: `Resource not found: ${errnoException.path}` }) diff --git a/packages/opencode/test/tool/bash.test.ts b/packages/opencode/test/tool/bash.test.ts index 3a74cba44..2919ccb02 100644 --- a/packages/opencode/test/tool/bash.test.ts +++ b/packages/opencode/test/tool/bash.test.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from "bun:test" import path from "path" import { BashTool } from "../../src/tool/bash" -import { Log } from "../../src/util/log" import { Instance } from "../../src/project/instance" const ctx = { @@ -15,7 +14,6 @@ const ctx = { const bash = await BashTool.init() const projectRoot = path.join(__dirname, "../..") -Log.init({ print: false }) describe("tool.bash", () => { test("basic", async () => { diff --git a/packages/opencode/test/tool/patch.test.ts b/packages/opencode/test/tool/patch.test.ts index 5defc0f52..649119dce 100644 --- a/packages/opencode/test/tool/patch.test.ts +++ b/packages/opencode/test/tool/patch.test.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from "bun:test" import path from "path" import { PatchTool } from "../../src/tool/patch" -import { Log } from "../../src/util/log" import { Instance } from "../../src/project/instance" import { tmpdir } from "../fixture/fixture" import * as fs from "fs/promises" @@ -10,51 +9,46 @@ const ctx = { sessionID: "test", messageID: "", toolCallID: "", - agent: "build", + agent: "build", abort: AbortSignal.any([]), metadata: () => {}, } const patchTool = await PatchTool.init() -Log.init({ print: false }) describe("tool.patch", () => { test("should validate required parameters", async () => { await Instance.provide({ directory: "/tmp", fn: async () => { - await expect( - patchTool.execute({ patchText: "" }, ctx) - ).rejects.toThrow("patchText is required") + await expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required") }, }) }) - + test("should validate patch format", async () => { await Instance.provide({ directory: "/tmp", fn: async () => { - await expect( - patchTool.execute({ patchText: "invalid patch" }, ctx) - ).rejects.toThrow("Failed to parse patch") + await expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow("Failed to parse patch") }, }) }) - + test("should handle empty patch", async () => { await Instance.provide({ directory: "/tmp", fn: async () => { const emptyPatch = `*** Begin Patch *** End Patch` - - await expect( - patchTool.execute({ patchText: emptyPatch }, ctx) - ).rejects.toThrow("No file changes found in patch") + + await expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow( + "No file changes found in patch", + ) }, }) }) - + test("should reject files outside working directory", async () => { await Instance.provide({ directory: "/tmp", @@ -63,17 +57,17 @@ describe("tool.patch", () => { *** Add File: /etc/passwd +malicious content *** End Patch` - - await expect( - patchTool.execute({ patchText: maliciousPatch }, ctx) - ).rejects.toThrow("is not in the current working directory") + + await expect(patchTool.execute({ patchText: maliciousPatch }, ctx)).rejects.toThrow( + "is not in the current working directory", + ) }, }) }) - + test("should handle simple add file operation", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -82,13 +76,13 @@ describe("tool.patch", () => { +Hello World +This is a test file *** End Patch` - + const result = await patchTool.execute({ patchText }, ctx) - + expect(result.title).toContain("files changed") expect(result.metadata.diff).toBeDefined() expect(result.output).toContain("Patch applied successfully") - + // Verify file was created const filePath = path.join(fixture.path, "test-file.txt") const content = await fs.readFile(filePath, "utf-8") @@ -96,10 +90,10 @@ describe("tool.patch", () => { }, }) }) - + test("should handle file with context update", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -109,24 +103,24 @@ describe("tool.patch", () => { +const DEBUG = false +const VERSION = "1.0" *** End Patch` - + const result = await patchTool.execute({ patchText }, ctx) - + expect(result.title).toContain("files changed") expect(result.metadata.diff).toBeDefined() expect(result.output).toContain("Patch applied successfully") - + // Verify file was created with correct content const filePath = path.join(fixture.path, "config.js") const content = await fs.readFile(filePath, "utf-8") - expect(content).toBe("const API_KEY = \"test-key\"\nconst DEBUG = false\nconst VERSION = \"1.0\"") + expect(content).toBe('const API_KEY = "test-key"\nconst DEBUG = false\nconst VERSION = "1.0"') }, }) }) - + test("should handle multiple file operations", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -138,13 +132,13 @@ describe("tool.patch", () => { *** Add File: file3.txt +Content of file 3 *** End Patch` - + const result = await patchTool.execute({ patchText }, ctx) - + expect(result.title).toContain("3 files changed") expect(result.metadata.diff).toBeDefined() expect(result.output).toContain("Patch applied successfully") - + // Verify all files were created for (let i = 1; i <= 3; i++) { const filePath = path.join(fixture.path, `file${i}.txt`) @@ -154,10 +148,10 @@ describe("tool.patch", () => { }, }) }) - + test("should create parent directories when adding nested files", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -165,26 +159,29 @@ describe("tool.patch", () => { *** Add File: deep/nested/file.txt +Deep nested content *** End Patch` - + const result = await patchTool.execute({ patchText }, ctx) - + expect(result.title).toContain("files changed") expect(result.output).toContain("Patch applied successfully") - + // Verify nested file was created const nestedPath = path.join(fixture.path, "deep", "nested", "file.txt") - const exists = await fs.access(nestedPath).then(() => true).catch(() => false) + const exists = await fs + .access(nestedPath) + .then(() => true) + .catch(() => false) expect(exists).toBe(true) - + const content = await fs.readFile(nestedPath, "utf-8") expect(content).toBe("Deep nested content") }, }) }) - + test("should generate proper unified diff in metadata", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -195,9 +192,9 @@ describe("tool.patch", () => { +line 2 +line 3 *** End Patch` - + await patchTool.execute({ patchText: patchText1 }, ctx) - + // Now create an update patch const patchText2 = `*** Begin Patch *** Update File: test.txt @@ -207,9 +204,9 @@ describe("tool.patch", () => { +line 2 updated line 3 *** End Patch` - + const result = await patchTool.execute({ patchText: patchText2 }, ctx) - + expect(result.metadata.diff).toBeDefined() expect(result.metadata.diff).toContain("@@") expect(result.metadata.diff).toContain("-line 2") @@ -217,10 +214,10 @@ describe("tool.patch", () => { }, }) }) - + test("should handle complex patch with multiple operations", async () => { await using fixture = await tmpdir() - + await Instance.provide({ directory: fixture.path, fn: async () => { @@ -238,26 +235,26 @@ describe("tool.patch", () => { + "debug": true +} *** End Patch` - + const result = await patchTool.execute({ patchText }, ctx) - + expect(result.title).toContain("3 files changed") expect(result.metadata.diff).toBeDefined() expect(result.output).toContain("Patch applied successfully") - + // Verify all files were created const newPath = path.join(fixture.path, "new.txt") const newContent = await fs.readFile(newPath, "utf-8") expect(newContent).toBe("This is a new file\nwith multiple lines") - + const existingPath = path.join(fixture.path, "existing.txt") const existingContent = await fs.readFile(existingPath, "utf-8") expect(existingContent).toBe("old content\nnew line\nmore content") - + const configPath = path.join(fixture.path, "config.json") const configContent = await fs.readFile(configPath, "utf-8") expect(configContent).toBe('{\n "version": "1.0",\n "debug": true\n}') }, }) }) -}) \ No newline at end of file +}) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 00e9fd9fe..0cd0d48c9 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "0.15.15", + "version": "0.15.16", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 85d2f2e18..d8c1257bb 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "0.15.15", + "version": "0.15.16", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 039b107a8..e49e9ddfc 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -614,8 +614,9 @@ export type UserMessage = { created: number } summary?: { + title?: string + body?: string diffs: Array - text: string } } diff --git a/packages/slack/package.json b/packages/slack/package.json index 1df72e2d6..26a3902e5 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "0.15.15", + "version": "0.15.16", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index 71bc76782..2ccd1f2d7 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "0.15.15", + "version": "0.15.16", "type": "module", "exports": { ".": "./src/components/index.ts", diff --git a/packages/web/package.json b/packages/web/package.json index e9ce1245c..761450f45 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "0.15.15", + "version": "0.15.16", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index da7d5dde8..97aadab57 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -28,6 +28,7 @@ OpenCode comes with several built-in LSP servers for popular languages: | svelte | .svelte | Auto-installs for Svelte projects | | astro | .astro | Auto-installs for Astro projects | | jdtls | .java | `Java SDK (version 21+)` installed | +| lua-ls | .lua | Auto-installs for Lua projects | LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met. diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 2094f29c6..8eae02660 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "0.15.15", + "version": "0.15.16", "publisher": "sst-dev", "repository": { "type": "git",