feat(cli): support both oh-my-opencode and oh-my-openagent package names
Update CLI config manager to detect and handle both legacy (oh-my-opencode)
and new (oh-my-openagent) package names during installation. Migration
will automatically replace old plugin entries with the new name.
🤖 Generated with assistance of OhMyOpenCode
This commit is contained in:
@@ -7,7 +7,8 @@ import { detectConfigFormat } from "./opencode-config-format"
|
|||||||
import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file"
|
import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file"
|
||||||
import { getPluginNameWithVersion } from "./plugin-name-with-version"
|
import { getPluginNameWithVersion } from "./plugin-name-with-version"
|
||||||
|
|
||||||
const PACKAGE_NAME = "oh-my-opencode"
|
const OLD_PACKAGE_NAME = "oh-my-opencode"
|
||||||
|
const NEW_PACKAGE_NAME = "oh-my-openagent"
|
||||||
|
|
||||||
export async function addPluginToOpenCodeConfig(currentVersion: string): Promise<ConfigMergeResult> {
|
export async function addPluginToOpenCodeConfig(currentVersion: string): Promise<ConfigMergeResult> {
|
||||||
try {
|
try {
|
||||||
@@ -21,7 +22,7 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { format, path } = detectConfigFormat()
|
const { format, path } = detectConfigFormat()
|
||||||
const pluginEntry = await getPluginNameWithVersion(currentVersion)
|
const pluginEntry = await getPluginNameWithVersion(currentVersion, NEW_PACKAGE_NAME)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (format === "none") {
|
if (format === "none") {
|
||||||
@@ -41,7 +42,13 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise
|
|||||||
|
|
||||||
const config = parseResult.config
|
const config = parseResult.config
|
||||||
const plugins = config.plugin ?? []
|
const plugins = config.plugin ?? []
|
||||||
const existingIndex = plugins.findIndex((p) => p === PACKAGE_NAME || p.startsWith(`${PACKAGE_NAME}@`))
|
const existingIndex = plugins.findIndex(
|
||||||
|
(p) =>
|
||||||
|
p === OLD_PACKAGE_NAME ||
|
||||||
|
p.startsWith(`${OLD_PACKAGE_NAME}@`) ||
|
||||||
|
p === NEW_PACKAGE_NAME ||
|
||||||
|
p.startsWith(`${NEW_PACKAGE_NAME}@`)
|
||||||
|
)
|
||||||
|
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
if (plugins[existingIndex] === pluginEntry) {
|
if (plugins[existingIndex] === pluginEntry) {
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ hasKimiForCoding: false,
|
|||||||
|
|
||||||
const openCodeConfig = parseResult.config
|
const openCodeConfig = parseResult.config
|
||||||
const plugins = openCodeConfig.plugin ?? []
|
const plugins = openCodeConfig.plugin ?? []
|
||||||
result.isInstalled = plugins.some((p) => p.startsWith("oh-my-opencode"))
|
const OLD_PACKAGE_NAME = "oh-my-opencode"
|
||||||
|
const NEW_PACKAGE_NAME = "oh-my-openagent"
|
||||||
|
result.isInstalled = plugins.some((p) => p.startsWith(OLD_PACKAGE_NAME) || p.startsWith(NEW_PACKAGE_NAME))
|
||||||
|
|
||||||
if (!result.isInstalled) {
|
if (!result.isInstalled) {
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
|
||||||
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||||
|
import { tmpdir } from "node:os"
|
||||||
|
import { join } from "node:path"
|
||||||
|
|
||||||
|
import { resetConfigContext } from "./config-context"
|
||||||
|
import { detectCurrentConfig } from "./detect-current-config"
|
||||||
|
import { addPluginToOpenCodeConfig } from "./add-plugin-to-opencode-config"
|
||||||
|
|
||||||
|
describe("detectCurrentConfig - dual name detection", () => {
|
||||||
|
let testConfigDir = ""
|
||||||
|
let testConfigPath = ""
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testConfigDir = join(tmpdir(), `omo-detect-config-${Date.now()}-${Math.random().toString(36).slice(2)}`)
|
||||||
|
testConfigPath = join(testConfigDir, "opencode.json")
|
||||||
|
|
||||||
|
mkdirSync(testConfigDir, { recursive: true })
|
||||||
|
process.env.OPENCODE_CONFIG_DIR = testConfigDir
|
||||||
|
resetConfigContext()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
rmSync(testConfigDir, { recursive: true, force: true })
|
||||||
|
resetConfigContext()
|
||||||
|
delete process.env.OPENCODE_CONFIG_DIR
|
||||||
|
})
|
||||||
|
|
||||||
|
it("detects oh-my-opencode in plugin array", () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-opencode"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectCurrentConfig()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.isInstalled).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("detects oh-my-openagent in plugin array", () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-openagent"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectCurrentConfig()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.isInstalled).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("detects oh-my-opencode with version pin", () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-opencode@3.11.0"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectCurrentConfig()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.isInstalled).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("detects oh-my-openagent with version pin", () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-openagent@3.12.0"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectCurrentConfig()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.isInstalled).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns false when plugin not present", () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["some-other-plugin"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = detectCurrentConfig()
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.isInstalled).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("addPluginToOpenCodeConfig - dual name detection", () => {
|
||||||
|
let testConfigDir = ""
|
||||||
|
let testConfigPath = ""
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testConfigDir = join(tmpdir(), `omo-add-plugin-${Date.now()}-${Math.random().toString(36).slice(2)}`)
|
||||||
|
testConfigPath = join(testConfigDir, "opencode.json")
|
||||||
|
|
||||||
|
mkdirSync(testConfigDir, { recursive: true })
|
||||||
|
process.env.OPENCODE_CONFIG_DIR = testConfigDir
|
||||||
|
resetConfigContext()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
rmSync(testConfigDir, { recursive: true, force: true })
|
||||||
|
resetConfigContext()
|
||||||
|
delete process.env.OPENCODE_CONFIG_DIR
|
||||||
|
})
|
||||||
|
|
||||||
|
it("finds and replaces old oh-my-opencode with new name", async () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-opencode"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
expect(savedConfig.plugin).not.toContain("oh-my-opencode")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("finds and replaces oh-my-openagent with new name", async () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-openagent"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
expect(savedConfig.plugin).not.toContain("oh-my-opencode")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("finds and replaces version-pinned oh-my-opencode@X.Y.Z", async () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-opencode@3.10.0"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
expect(savedConfig.plugin).not.toContain("oh-my-opencode@3.10.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("finds and replaces version-pinned oh-my-openagent@X.Y.Z", async () => {
|
||||||
|
// given
|
||||||
|
const config = { plugin: ["oh-my-openagent@3.10.0"] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
expect(savedConfig.plugin).not.toContain("oh-my-openagent@3.10.0")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adds new plugin when none exists", async () => {
|
||||||
|
// given - no plugin array
|
||||||
|
const config = {}
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adds plugin when plugin array is empty", async () => {
|
||||||
|
// given - empty plugin array
|
||||||
|
const config = { plugin: [] }
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.11.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toContain("oh-my-openagent")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,28 +1,33 @@
|
|||||||
import { fetchNpmDistTags } from "./npm-dist-tags"
|
import { fetchNpmDistTags } from "./npm-dist-tags"
|
||||||
|
|
||||||
const PACKAGE_NAME = "oh-my-opencode"
|
const DEFAULT_PACKAGE_NAME = "oh-my-opencode"
|
||||||
|
const NEW_PACKAGE_NAME = "oh-my-openagent"
|
||||||
const PRIORITIZED_TAGS = ["latest", "beta", "next"] as const
|
const PRIORITIZED_TAGS = ["latest", "beta", "next"] as const
|
||||||
|
|
||||||
function getFallbackEntry(version: string): string {
|
function getFallbackEntry(version: string, packageName: string): string {
|
||||||
const prereleaseMatch = version.match(/-([a-zA-Z][a-zA-Z0-9-]*)(?:\.|$)/)
|
const prereleaseMatch = version.match(/-([a-zA-Z][a-zA-Z0-9-]*)(?:\.|$)/)
|
||||||
if (prereleaseMatch) {
|
if (prereleaseMatch) {
|
||||||
return `${PACKAGE_NAME}@${prereleaseMatch[1]}`
|
return `${packageName}@${prereleaseMatch[1]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return PACKAGE_NAME
|
return packageName
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginNameWithVersion(currentVersion: string): Promise<string> {
|
export async function getPluginNameWithVersion(
|
||||||
const distTags = await fetchNpmDistTags(PACKAGE_NAME)
|
currentVersion: string,
|
||||||
|
packageName: string = DEFAULT_PACKAGE_NAME
|
||||||
|
): Promise<string> {
|
||||||
|
const distTags = await fetchNpmDistTags(NEW_PACKAGE_NAME)
|
||||||
|
|
||||||
|
|
||||||
if (distTags) {
|
if (distTags) {
|
||||||
const allTags = new Set([...PRIORITIZED_TAGS, ...Object.keys(distTags)])
|
const allTags = new Set([...PRIORITIZED_TAGS, ...Object.keys(distTags)])
|
||||||
for (const tag of allTags) {
|
for (const tag of allTags) {
|
||||||
if (distTags[tag] === currentVersion) {
|
if (distTags[tag] === currentVersion) {
|
||||||
return `${PACKAGE_NAME}@${tag}`
|
return `${packageName}@${tag}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getFallbackEntry(currentVersion)
|
return getFallbackEntry(currentVersion, packageName)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user