diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index b0f7d59447db..ae68a477dc55 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -2,7 +2,9 @@ "$schema": "https://opencode.ai/config.json", "provider": {}, "permission": {}, - "references": { + // TODO: flip back to `references` once a release containing the v1 `reference` migration ships. + // The release pipeline runs the latest published opencode against this file, which only knows `reference`. + "reference": { "effect": { "repository": "github.com/Effect-TS/effect-smol", "description": "Use for Effect v4 and effect-smol implementation details", diff --git a/packages/core/src/v1/config/config.ts b/packages/core/src/v1/config/config.ts index 5c520846da8f..2e773f71e256 100644 --- a/packages/core/src/v1/config/config.ts +++ b/packages/core/src/v1/config/config.ts @@ -45,6 +45,9 @@ export const Info = Schema.Struct({ references: Schema.optional(ConfigReference.Info).annotate({ description: "Named git or local directory references", }), + reference: Schema.optional(ConfigReference.Info).annotate({ + description: "@deprecated Use 'references' field instead. Named git or local directory references", + }), watcher: Schema.optional(Schema.Struct({ ignore: Schema.optional(Schema.mutable(Schema.Array(Schema.String))) })), snapshot: Schema.optional(Schema.Boolean).annotate({ description: diff --git a/packages/core/src/v1/config/migrate.ts b/packages/core/src/v1/config/migrate.ts index 19bf48aa744d..3b4f13868edc 100644 --- a/packages/core/src/v1/config/migrate.ts +++ b/packages/core/src/v1/config/migrate.ts @@ -12,6 +12,7 @@ const keys = new Set([ "logLevel", "server", "command", + "reference", "snapshot", "plugin", "autoshare", @@ -62,7 +63,7 @@ export function migrate(info: typeof ConfigV1.Info.Type) { skills: info.skills && [...(info.skills.paths ?? []), ...(info.skills.urls ?? [])], commands: info.command, instructions: info.instructions, - references: info.references, + references: info.references ?? info.reference, plugins: info.plugin?.map((plugin) => typeof plugin === "string" ? plugin : { package: plugin[0], options: plugin[1] }, ), diff --git a/packages/core/test/config/config.test.ts b/packages/core/test/config/config.test.ts index 5f62cbce6192..6275d8fed350 100644 --- a/packages/core/test/config/config.test.ts +++ b/packages/core/test/config/config.test.ts @@ -70,7 +70,9 @@ describe("Config", () => { Effect.sync(() => { expect(ConfigMigrateV1.isV1({ snapshot: false })).toBe(true) expect(ConfigMigrateV1.isV1({ snapshot: false, agents: {} })).toBe(true) + expect(ConfigMigrateV1.isV1({ reference: {} })).toBe(true) expect(ConfigMigrateV1.isV1({ shell: "/bin/zsh", model: "anthropic/claude" })).toBe(false) + expect(ConfigMigrateV1.isV1({ references: {} })).toBe(false) }), ) @@ -431,6 +433,42 @@ describe("Config", () => { ), ) + it.live("migrates the deprecated reference key into references", () => + Effect.acquireRelease( + Effect.promise(() => tmpdir()), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ).pipe( + Effect.flatMap((tmp) => + Effect.gen(function* () { + yield* Effect.promise(() => + fs.writeFile( + path.join(tmp.path, "opencode.json"), + JSON.stringify({ + reference: { + local: { path: "../library" }, + sdk: { repository: "github.com/example/sdk", branch: "main" }, + shorthand: "github.com/example/docs", + }, + }), + ), + ) + + return yield* Effect.gen(function* () { + const config = yield* Config.Service + const documents = (yield* config.entries()).filter((entry) => entry.type === "document") + + expect(documents).toHaveLength(1) + expect(documents[0]?.info.references).toEqual({ + local: { path: "../library" }, + sdk: { repository: "github.com/example/sdk", branch: "main" }, + shorthand: "github.com/example/docs", + }) + }).pipe(Effect.provide(testLayer(tmp.path))) + }), + ), + ), + ) + it.live("migrates v1 configuration when a v1-only key is present", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index c016431bc781..f4a0cd4a2817 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -722,6 +722,26 @@ it.instance("migrates mode field to agent field", () => }), ) +it.instance("accepts the deprecated reference field", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* writeConfigEffect(test.directory, { + $schema: "https://opencode.ai/config.json", + reference: { + local: { path: "../library" }, + sdk: { repository: "github.com/example/sdk", branch: "main" }, + shorthand: "github.com/example/docs", + }, + }) + const config = yield* Config.use.get() + expect(config.reference).toEqual({ + local: { path: "../library" }, + sdk: { repository: "github.com/example/sdk", branch: "main" }, + shorthand: "github.com/example/docs", + }) + }), +) + it.instance("loads config from .opencode directory", () => Effect.gen(function* () { const test = yield* TestInstance diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 9e9c74c6ec38..7f5e765a41aa 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1931,6 +1931,9 @@ export type Config = { references?: { [key: string]: string | ConfigV2ReferenceGit | ConfigV2ReferenceLocal } + reference?: { + [key: string]: string | ConfigV2ReferenceGit | ConfigV2ReferenceLocal + } watcher?: { ignore?: Array }