diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index cbbab479e145..f46093dbda8d 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -7,6 +7,9 @@ on: pull_request: workflow_dispatch: +permissions: + contents: write + jobs: generate: runs-on: blacksmith-4vcpu-ubuntu-2404 @@ -28,6 +31,7 @@ jobs: run: ./script/generate.ts - name: Commit and push + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository run: | if [ -z "$(git status --porcelain)" ]; then echo "No changes to commit" @@ -38,15 +42,19 @@ jobs: git add -A git commit -m "chore: generate" git push origin HEAD:${{ github.ref_name }} --no-verify - # if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then - # echo "" - # echo "============================================" - # echo "Failed to push generated code." - # echo "Please run locally and push:" - # echo "" - # echo " ./script/generate.ts" - # echo " git add -A && git commit -m \"chore: generate\" && git push" - # echo "" - # echo "============================================" - # exit 1 - # fi + + - name: Check for generated changes + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "" + echo "============================================" + echo "There are uncommitted generated changes." + echo "Please run locally and push:" + echo "" + echo " ./script/generate.ts" + echo " git add -A && git commit -m \"chore: generate\" && git push" + echo "" + echo "============================================" + exit 1 + fi diff --git a/bun.lock b/bun.lock index fa4d84e4ec36..53c06d940afe 100644 --- a/bun.lock +++ b/bun.lock @@ -18,7 +18,7 @@ "prettier": "3.6.2", "semver": "^7.6.0", "sst": "3.17.23", - "turbo": "2.5.6", + "turbo": "2.8.0", }, }, "packages/app": { @@ -3743,19 +3743,19 @@ "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], - "turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="], + "turbo": ["turbo@2.8.0", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.0", "turbo-darwin-arm64": "2.8.0", "turbo-linux-64": "2.8.0", "turbo-linux-arm64": "2.8.0", "turbo-windows-64": "2.8.0", "turbo-windows-arm64": "2.8.0" }, "bin": { "turbo": "bin/turbo" } }, "sha512-hYbxnLEdvJF+DLALS+Ia+PbfNtn0sDP0hH2u9AFoskSUDmcVHSrtwHpzdX94MrRJKo9D9tYxY3MyP20gnlrWyA=="], - "turbo-darwin-64": ["turbo-darwin-64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A=="], + "turbo-darwin-64": ["turbo-darwin-64@2.8.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N7f4PYqz25yk8c5kituk09bJ89tE4wPPqKXgYccT6nbEQnGnrdvlyCHLyqViNObTgjjrddqjb1hmDkv7VcxE0g=="], - "turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA=="], + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eVzejaP5fn51gmJAPW68U6mSjFaAZ26rPiE36mMdk+tMC4XBGmJHT/fIgrhcrXMvINCl27RF8VmguRe+MBlSuQ=="], - "turbo-linux-64": ["turbo-linux-64@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA=="], + "turbo-linux-64": ["turbo-linux-64@2.8.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ILR45zviYae3icf4cmUISdj8X17ybNcMh3Ms4cRdJF5sS50qDDTv8qeWqO/lPeHsu6r43gVWDofbDZYVuXYL7Q=="], - "turbo-linux-arm64": ["turbo-linux-arm64@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ=="], + "turbo-linux-arm64": ["turbo-linux-arm64@2.8.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-z9pUa8ENFuHmadPfjEYMRWlXO82t1F/XBDx2XTg+cWWRZHf85FnEB6D4ForJn/GoKEEvwdPhFLzvvhOssom2ug=="], - "turbo-windows-64": ["turbo-windows-64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg=="], + "turbo-windows-64": ["turbo-windows-64@2.8.0", "", { "os": "win32", "cpu": "x64" }, "sha512-J6juRSRjmSErEqJCv7nVIq2DgZ2NHXqyeV8NQTFSyIvrThKiWe7FDOO6oYpuR06+C1NW82aoN4qQt4/gYvz25w=="], - "turbo-windows-arm64": ["turbo-windows-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q=="], + "turbo-windows-arm64": ["turbo-windows-arm64@2.8.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-qarBZvCu6uka35739TS+y/3CBU3zScrVAfohAkKHG+So+93Wn+5tKArs8HrO2fuTaGou8fMIeTV7V5NgzCVkSQ=="], "turndown": ["turndown@7.2.0", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A=="], diff --git a/package.json b/package.json index 4267ef645661..1d97dcaf0847 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "prettier": "3.6.2", "semver": "^7.6.0", "sst": "3.17.23", - "turbo": "2.5.6" + "turbo": "2.8.0" }, "dependencies": { "@aws-sdk/client-s3": "3.933.0", diff --git a/packages/opencode/src/command/index.ts b/packages/opencode/src/command/index.ts index 976f1cd51e96..e5a8bd487832 100644 --- a/packages/opencode/src/command/index.ts +++ b/packages/opencode/src/command/index.ts @@ -6,6 +6,7 @@ import { Identifier } from "../id/id" import PROMPT_INITIALIZE from "./template/initialize.txt" import PROMPT_REVIEW from "./template/review.txt" import { MCP } from "../mcp" +import { Flag } from "../flag/flag" export namespace Command { export const Event = { @@ -53,6 +54,7 @@ export namespace Command { export const Default = { INIT: "init", REVIEW: "review", + ENV: "env", } as const const state = Instance.state(async () => { @@ -76,6 +78,15 @@ export namespace Command { subtask: true, hints: hints(PROMPT_REVIEW), }, + // added for branch:feature/model-env-vars + [Default.ENV]: { + name: Default.ENV, + description: "display Opencode environment variables", + get template() { + return `echo $(env | grep OPENCODE_)` + }, + hints: [], + }, } for (const [name, command] of Object.entries(cfg.command ?? {})) { diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 9084bf444362..a58cee51d5d8 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -29,6 +29,13 @@ export namespace Flag { export const OPENCODE_SERVER_PASSWORD = process.env["OPENCODE_SERVER_PASSWORD"] export const OPENCODE_SERVER_USERNAME = process.env["OPENCODE_SERVER_USERNAME"] + // added for branch:feature/model-env-vars + export const OPENCODE_MODEL_NAME = process.env["OPENCODE_MODEL_NAME"] + export const OPENCODE_MODEL_ID = process.env["OPENCODE_MODEL_ID"] + export const OPENCODE_MODEL_CANONICAL_ID = process.env["OPENCODE_MODEL_CANONICAL_ID"] + export const OPENCODE_MODEL_PROVIDER_ID = process.env["OPENCODE_MODEL_PROVIDER_ID"] + export const OPENCODE_MODEL_FULL_ID = process.env["OPENCODE_MODEL_FULL_ID"] + // Experimental export const OPENCODE_EXPERIMENTAL = truthy("OPENCODE_EXPERIMENTAL") export const OPENCODE_EXPERIMENTAL_FILEWATCHER = truthy("OPENCODE_EXPERIMENTAL_FILEWATCHER") diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index 73474ed4f877..1a03ac86071d 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -8,6 +8,10 @@ import type { WSContext } from "hono/ws" import { Instance } from "../project/instance" import { lazy } from "@opencode-ai/util/lazy" import { Shell } from "@/shell/shell" +import { Global } from "@/global" +import { Provider } from "../provider/provider" +import { Config } from "../config/config" +import { Filesystem } from "@/util/filesystem" export namespace Pty { const log = Log.create({ service: "pty" }) @@ -102,12 +106,48 @@ export namespace Pty { } const cwd = input.cwd || Instance.directory - const env = { - ...process.env, - ...input.env, - TERM: "xterm-256color", - OPENCODE_TERMINAL: "1", - } as Record + const env = { ...process.env, ...input.env, TERM: "xterm-256color" } as Record + + try { + const statePath = `${Global.Path.state}/model.json` + let recent: { providerID: string; modelID: string } | undefined + if (await Filesystem.exists(statePath)) { + const content = await Bun.file(statePath).text() + const json = JSON.parse(content) + if (json.recent && Array.isArray(json.recent) && json.recent.length > 0 && json.recent[0].providerID) { + recent = json.recent[0] + } + } + + const config = await Config.get() + const configModel = config.model + + let providerID: string + let modelID: string + + if (recent) { + providerID = recent.providerID + modelID = recent.modelID + } else if (configModel) { + const parsed = Provider.parseModel(configModel) + providerID = parsed.providerID + modelID = parsed.modelID + } else { + const defaultModel = await Provider.defaultModel() + providerID = defaultModel.providerID + modelID = defaultModel.modelID + } + + const model = await Provider.getModel(providerID, modelID) + + const canonicalModelID = model.id.split("/").pop() || model.id + env["OPENCODE_MODEL_ID"] = model.id + env["OPENCODE_MODEL_CANONICAL_ID"] = canonicalModelID + env["OPENCODE_MODEL_PROVIDER_ID"] = model.providerID + env["OPENCODE_MODEL_FULL_ID"] = `${model.providerID}/${model.id}` + env["OPENCODE_MODEL_NAME"] = model.name + } catch (error) {} + log.info("creating session", { id, cmd: command, args, cwd }) const spawn = await pty() diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts index c93b74b9a406..41029ecbbdb5 100644 --- a/packages/opencode/src/question/index.ts +++ b/packages/opencode/src/question/index.ts @@ -10,7 +10,7 @@ export namespace Question { export const Option = z .object({ - label: z.string().describe("Display text (1-5 words, concise)"), + label: z.string().max(30).describe("Display text (1-5 words, concise)"), description: z.string().describe("Explanation of choice"), }) .meta({ @@ -21,7 +21,7 @@ export namespace Question { export const Info = z .object({ question: z.string().describe("Complete question"), - header: z.string().describe("Very short label (max 30 chars)"), + header: z.string().max(30).describe("Very short label (max 30 chars)"), options: z.array(Option).describe("Available choices"), multiple: z.boolean().optional().describe("Allow selecting multiple choices"), custom: z.boolean().optional().describe("Allow typing a custom answer (default: true)"), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 94eabdef7f48..e49722564c42 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -45,7 +45,9 @@ import { SessionStatus } from "./status" import { LLM } from "./llm" import { iife } from "@/util/iife" import { Shell } from "@/shell/shell" -import { Truncate } from "@/tool/truncation" +import { Global } from "@/global" +import { Filesystem } from "@/util/filesystem" +import { Config } from "../config/config" // @ts-ignore globalThis.AI_SDK_LOG_WARNINGS = false @@ -804,17 +806,10 @@ export namespace SessionPrompt { } } - const truncated = await Truncate.output(textParts.join("\n\n"), {}, input.agent) - const metadata = { - ...(result.metadata ?? {}), - truncated: truncated.truncated, - ...(truncated.truncated && { outputPath: truncated.outputPath }), - } - return { title: "", - metadata, - output: truncated.content, + metadata: result.metadata ?? {}, + output: textParts.join("\n\n"), attachments, content: result.content, // directly return content to preserve ordering when outputting to model } @@ -1434,6 +1429,47 @@ NOTE: At any point in time through this workflow you should feel free to ask the process.platform === "win32" ? path.win32.basename(shell, ".exe") : path.basename(shell) ).toLowerCase() + const statePath = `${Global.Path.state}/model.json` + let envPrefix = "" + let modelInfo: { providerID: string; modelID: string; canonicalModelID: string; id: string; name: string } | null = + null + try { + let recent: { providerID: string; modelID: string } | undefined + if (await Filesystem.exists(statePath)) { + const content = await Bun.file(statePath).text() + const json = JSON.parse(content) + if (json.recent && Array.isArray(json.recent) && json.recent.length > 0 && json.recent[0].providerID) { + recent = json.recent[0] + } + } + + const config = await Config.get() + const configModel = config.model + + let providerID: string + let modelID: string + + if (recent) { + providerID = recent.providerID + modelID = recent.modelID + } else if (configModel) { + const parsed = Provider.parseModel(configModel) + providerID = parsed.providerID + modelID = parsed.modelID + } else { + const defaultModel = await Provider.defaultModel() + providerID = defaultModel.providerID + modelID = defaultModel.modelID + } + + const model = await Provider.getModel(providerID, modelID) + + const canonicalModelID = model.id.split("/").pop() || model.id + const escapedName = model.name.replace(/'/g, "'\\''") + modelInfo = { providerID: model.providerID, modelID: model.id, canonicalModelID, id: model.id, name: model.name } + envPrefix = `export OPENCODE_MODEL_ID='${model.id}' OPENCODE_MODEL_CANONICAL_ID='${canonicalModelID}' OPENCODE_MODEL_PROVIDER_ID='${model.providerID}' OPENCODE_MODEL_FULL_ID='${model.providerID}/${model.id}' OPENCODE_MODEL_NAME='${escapedName}'; ` + } catch (error) {} + const invocations: Record = { nu: { args: ["-c", input.command], @@ -1446,6 +1482,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the "-c", "-l", ` + ${envPrefix} [[ -f ~/.zshenv ]] && source ~/.zshenv >/dev/null 2>&1 || true [[ -f "\${ZDOTDIR:-$HOME}/.zshrc" ]] && source "\${ZDOTDIR:-$HOME}/.zshrc" >/dev/null 2>&1 || true eval ${JSON.stringify(input.command)} @@ -1457,39 +1494,44 @@ NOTE: At any point in time through this workflow you should feel free to ask the "-c", "-l", ` + ${envPrefix} shopt -s expand_aliases [[ -f ~/.bashrc ]] && source ~/.bashrc >/dev/null 2>&1 || true eval ${JSON.stringify(input.command)} `, ], }, - // Windows cmd cmd: { args: ["/c", input.command], }, - // Windows PowerShell powershell: { args: ["-NoProfile", "-Command", input.command], }, pwsh: { args: ["-NoProfile", "-Command", input.command], }, - // Fallback: any shell that doesn't match those above - // - No -l, for max compatibility "": { - args: ["-c", `${input.command}`], + args: ["-c", `${envPrefix}${input.command}`], }, } const matchingInvocation = invocations[shellName] ?? invocations[""] - const args = matchingInvocation?.args + const env = { ...process.env } + + if (modelInfo) { + env["OPENCODE_MODEL_ID"] = modelInfo.id + env["OPENCODE_MODEL_CANONICAL_ID"] = modelInfo.canonicalModelID + env["OPENCODE_MODEL_PROVIDER_ID"] = modelInfo.providerID + env["OPENCODE_MODEL_FULL_ID"] = `${modelInfo.providerID}/${modelInfo.id}` + env["OPENCODE_MODEL_NAME"] = modelInfo.name + } - const proc = spawn(shell, args, { + const proc = spawn(shell, matchingInvocation.args, { cwd: Instance.directory, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"], env: { - ...process.env, + ...env, TERM: "dumb", }, }) diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index bf7c524941fe..589600554fc2 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -8,6 +8,9 @@ import { Instance } from "../project/instance" import { lazy } from "@/util/lazy" import { Language } from "web-tree-sitter" +import { Global } from "@/global" +import { Provider } from "../provider/provider" +import { Config } from "../config/config" import { $ } from "bun" import { Filesystem } from "@/util/filesystem" import { fileURLToPath } from "url" @@ -154,12 +157,53 @@ export const BashTool = Tool.define("bash", async () => { }) } + const env = { ...process.env } + try { + const statePath = `${Global.Path.state}/model.json` + let recent: { providerID: string; modelID: string } | undefined + if (await Filesystem.exists(statePath)) { + const content = await Bun.file(statePath).text() + const json = JSON.parse(content) + if (json.recent && Array.isArray(json.recent) && json.recent.length > 0 && json.recent[0].providerID) { + recent = json.recent[0] + } + } + + const config = await Config.get() + const configModel = config.model + + let providerID: string + let modelID: string + + if (recent) { + providerID = recent.providerID + modelID = recent.modelID + } else if (configModel) { + const parsed = Provider.parseModel(configModel) + providerID = parsed.providerID + modelID = parsed.modelID + } else { + const defaultModel = await Provider.defaultModel() + providerID = defaultModel.providerID + modelID = defaultModel.modelID + } + + const model = await Provider.getModel(providerID, modelID) + + const canonicalModelID = model.id.split("/").pop() || model.id + env["OPENCODE_MODEL_ID"] = model.id + env["OPENCODE_MODEL_CANONICAL_ID"] = canonicalModelID + env["OPENCODE_MODEL_PROVIDER_ID"] = model.providerID + env["OPENCODE_MODEL_FULL_ID"] = `${model.providerID}/${model.id}` + env["OPENCODE_MODEL_NAME"] = model.name + } catch (error) { + // ignore + } + const proc = spawn(params.command, { shell, cwd, - env: { - ...process.env, - }, + env, stdio: ["ignore", "pipe", "pipe"], detached: process.platform !== "win32", }) diff --git a/packages/opencode/test/tool/question.test.ts b/packages/opencode/test/tool/question.test.ts index 4a436186db61..5f460782289e 100644 --- a/packages/opencode/test/tool/question.test.ts +++ b/packages/opencode/test/tool/question.test.ts @@ -64,44 +64,43 @@ describe("tool.question", () => { expect(result.output).toContain(`"What is your favorite animal?"="Dog"`) }) - // intentionally removed the zod validation due to tool call errors, hoping prompting is gonna be good enough - // test("should throw an Error for header exceeding 30 characters", async () => { - // const tool = await QuestionTool.init() - // const questions = [ - // { - // question: "What is your favorite animal?", - // header: "This Header is Definitely More Than Thirty Characters Long", - // options: [{ label: "Dog", description: "Man's best friend" }], - // }, - // ] - // try { - // await tool.execute({ questions }, ctx) - // // If it reaches here, the test should fail - // expect(true).toBe(false) - // } catch (e: any) { - // expect(e).toBeInstanceOf(Error) - // expect(e.cause).toBeInstanceOf(z.ZodError) - // } - // }) + test("should throw an Error for header exceeding 30 characters", async () => { + const tool = await QuestionTool.init() + const questions = [ + { + question: "What is your favorite animal?", + header: "This Header is Definitely More Than Thirty Characters Long", + options: [{ label: "Dog", description: "Man's best friend" }], + }, + ] + try { + await tool.execute({ questions }, ctx) + // If it reaches here, the test should fail + expect(true).toBe(false) + } catch (e: any) { + expect(e).toBeInstanceOf(Error) + expect(e.cause).toBeInstanceOf(z.ZodError) + } + }) - // test("should throw an Error for label exceeding 30 characters", async () => { - // const tool = await QuestionTool.init() - // const questions = [ - // { - // question: "A question with a very long label", - // header: "Long Label", - // options: [ - // { label: "This is a very, very, very long label that will exceed the limit", description: "A description" }, - // ], - // }, - // ] - // try { - // await tool.execute({ questions }, ctx) - // // If it reaches here, the test should fail - // expect(true).toBe(false) - // } catch (e: any) { - // expect(e).toBeInstanceOf(Error) - // expect(e.cause).toBeInstanceOf(z.ZodError) - // } - // }) + test("should throw an Error for label exceeding 30 characters", async () => { + const tool = await QuestionTool.init() + const questions = [ + { + question: "A question with a very long label", + header: "Long Label", + options: [ + { label: "This is a very, very, very long label that will exceed the limit", description: "A description" }, + ], + }, + ] + try { + await tool.execute({ questions }, ctx) + // If it reaches here, the test should fail + expect(true).toBe(false) + } catch (e: any) { + expect(e).toBeInstanceOf(Error) + expect(e.cause).toBeInstanceOf(z.ZodError) + } + }) }) diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index dc2e51e5fbaf..1520403acb73 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -7425,7 +7425,8 @@ "properties": { "label": { "description": "Display text (1-5 words, concise)", - "type": "string" + "type": "string", + "maxLength": 30 }, "description": { "description": "Explanation of choice", @@ -7443,7 +7444,8 @@ }, "header": { "description": "Very short label (max 30 chars)", - "type": "string" + "type": "string", + "maxLength": 30 }, "options": { "description": "Available choices", diff --git a/turbo.json b/turbo.json index 5de1b8d75172..a2c97cd83271 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://turborepo.com/schema.json", + "$schema": "https://v2-8-0.turborepo.dev/schema.json", "tasks": { "typecheck": {}, "build": {