Skip to content

Commit 0dcfcd3

Browse files
committed
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
1 parent 6aeda59 commit 0dcfcd3

File tree

4 files changed

+222
-12
lines changed

4 files changed

+222
-12
lines changed

src/cli/config-manager/add-plugin-to-opencode-config.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { detectConfigFormat } from "./opencode-config-format"
77
import { parseOpenCodeConfigFileWithError, type OpenCodeConfig } from "./parse-opencode-config-file"
88
import { getPluginNameWithVersion } from "./plugin-name-with-version"
99

10-
const PACKAGE_NAME = "oh-my-opencode"
10+
const OLD_PACKAGE_NAME = "oh-my-opencode"
11+
const NEW_PACKAGE_NAME = "oh-my-openagent"
1112

1213
export async function addPluginToOpenCodeConfig(currentVersion: string): Promise<ConfigMergeResult> {
1314
try {
@@ -21,7 +22,7 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise
2122
}
2223

2324
const { format, path } = detectConfigFormat()
24-
const pluginEntry = await getPluginNameWithVersion(currentVersion)
25+
const pluginEntry = await getPluginNameWithVersion(currentVersion, NEW_PACKAGE_NAME)
2526

2627
try {
2728
if (format === "none") {
@@ -41,7 +42,13 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise
4142

4243
const config = parseResult.config
4344
const plugins = config.plugin ?? []
44-
const existingIndex = plugins.findIndex((p) => p === PACKAGE_NAME || p.startsWith(`${PACKAGE_NAME}@`))
45+
const existingIndex = plugins.findIndex(
46+
(p) =>
47+
p === OLD_PACKAGE_NAME ||
48+
p.startsWith(`${OLD_PACKAGE_NAME}@`) ||
49+
p === NEW_PACKAGE_NAME ||
50+
p.startsWith(`${NEW_PACKAGE_NAME}@`)
51+
)
4552

4653
if (existingIndex !== -1) {
4754
if (plugins[existingIndex] === pluginEntry) {

src/cli/config-manager/detect-current-config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ hasKimiForCoding: false,
6161

6262
const openCodeConfig = parseResult.config
6363
const plugins = openCodeConfig.plugin ?? []
64-
result.isInstalled = plugins.some((p) => p.startsWith("oh-my-opencode"))
64+
const OLD_PACKAGE_NAME = "oh-my-opencode"
65+
const NEW_PACKAGE_NAME = "oh-my-openagent"
66+
result.isInstalled = plugins.some((p) => p.startsWith(OLD_PACKAGE_NAME) || p.startsWith(NEW_PACKAGE_NAME))
6567

6668
if (!result.isInstalled) {
6769
return result
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
2+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3+
import { tmpdir } from "node:os"
4+
import { join } from "node:path"
5+
6+
import { resetConfigContext } from "./config-context"
7+
import { detectCurrentConfig } from "./detect-current-config"
8+
import { addPluginToOpenCodeConfig } from "./add-plugin-to-opencode-config"
9+
10+
describe("detectCurrentConfig - dual name detection", () => {
11+
let testConfigDir = ""
12+
let testConfigPath = ""
13+
14+
beforeEach(() => {
15+
testConfigDir = join(tmpdir(), `omo-detect-config-${Date.now()}-${Math.random().toString(36).slice(2)}`)
16+
testConfigPath = join(testConfigDir, "opencode.json")
17+
18+
mkdirSync(testConfigDir, { recursive: true })
19+
process.env.OPENCODE_CONFIG_DIR = testConfigDir
20+
resetConfigContext()
21+
})
22+
23+
afterEach(() => {
24+
rmSync(testConfigDir, { recursive: true, force: true })
25+
resetConfigContext()
26+
delete process.env.OPENCODE_CONFIG_DIR
27+
})
28+
29+
it("detects oh-my-opencode in plugin array", () => {
30+
// given
31+
const config = { plugin: ["oh-my-opencode"] }
32+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
33+
34+
// when
35+
const result = detectCurrentConfig()
36+
37+
// then
38+
expect(result.isInstalled).toBe(true)
39+
})
40+
41+
it("detects oh-my-openagent in plugin array", () => {
42+
// given
43+
const config = { plugin: ["oh-my-openagent"] }
44+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
45+
46+
// when
47+
const result = detectCurrentConfig()
48+
49+
// then
50+
expect(result.isInstalled).toBe(true)
51+
})
52+
53+
it("detects oh-my-opencode with version pin", () => {
54+
// given
55+
const config = { plugin: ["[email protected]"] }
56+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
57+
58+
// when
59+
const result = detectCurrentConfig()
60+
61+
// then
62+
expect(result.isInstalled).toBe(true)
63+
})
64+
65+
it("detects oh-my-openagent with version pin", () => {
66+
// given
67+
const config = { plugin: ["[email protected]"] }
68+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
69+
70+
// when
71+
const result = detectCurrentConfig()
72+
73+
// then
74+
expect(result.isInstalled).toBe(true)
75+
})
76+
77+
it("returns false when plugin not present", () => {
78+
// given
79+
const config = { plugin: ["some-other-plugin"] }
80+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
81+
82+
// when
83+
const result = detectCurrentConfig()
84+
85+
// then
86+
expect(result.isInstalled).toBe(false)
87+
})
88+
})
89+
90+
describe("addPluginToOpenCodeConfig - dual name detection", () => {
91+
let testConfigDir = ""
92+
let testConfigPath = ""
93+
94+
beforeEach(() => {
95+
testConfigDir = join(tmpdir(), `omo-add-plugin-${Date.now()}-${Math.random().toString(36).slice(2)}`)
96+
testConfigPath = join(testConfigDir, "opencode.json")
97+
98+
mkdirSync(testConfigDir, { recursive: true })
99+
process.env.OPENCODE_CONFIG_DIR = testConfigDir
100+
resetConfigContext()
101+
})
102+
103+
afterEach(() => {
104+
rmSync(testConfigDir, { recursive: true, force: true })
105+
resetConfigContext()
106+
delete process.env.OPENCODE_CONFIG_DIR
107+
})
108+
109+
it("finds and replaces old oh-my-opencode with new name", async () => {
110+
// given
111+
const config = { plugin: ["oh-my-opencode"] }
112+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
113+
114+
// when
115+
const result = await addPluginToOpenCodeConfig("3.11.0")
116+
117+
// then
118+
expect(result.success).toBe(true)
119+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
120+
expect(savedConfig.plugin).toContain("oh-my-openagent")
121+
expect(savedConfig.plugin).not.toContain("oh-my-opencode")
122+
})
123+
124+
it("finds and replaces oh-my-openagent with new name", async () => {
125+
// given
126+
const config = { plugin: ["oh-my-openagent"] }
127+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
128+
129+
// when
130+
const result = await addPluginToOpenCodeConfig("3.11.0")
131+
132+
// then
133+
expect(result.success).toBe(true)
134+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
135+
expect(savedConfig.plugin).toContain("oh-my-openagent")
136+
expect(savedConfig.plugin).not.toContain("oh-my-opencode")
137+
})
138+
139+
it("finds and replaces version-pinned [email protected]", async () => {
140+
// given
141+
const config = { plugin: ["[email protected]"] }
142+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
143+
144+
// when
145+
const result = await addPluginToOpenCodeConfig("3.11.0")
146+
147+
// then
148+
expect(result.success).toBe(true)
149+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
150+
expect(savedConfig.plugin).toContain("oh-my-openagent")
151+
expect(savedConfig.plugin).not.toContain("[email protected]")
152+
})
153+
154+
it("finds and replaces version-pinned [email protected]", async () => {
155+
// given
156+
const config = { plugin: ["[email protected]"] }
157+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
158+
159+
// when
160+
const result = await addPluginToOpenCodeConfig("3.11.0")
161+
162+
// then
163+
expect(result.success).toBe(true)
164+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
165+
expect(savedConfig.plugin).toContain("oh-my-openagent")
166+
expect(savedConfig.plugin).not.toContain("[email protected]")
167+
})
168+
169+
it("adds new plugin when none exists", async () => {
170+
// given - no plugin array
171+
const config = {}
172+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
173+
174+
// when
175+
const result = await addPluginToOpenCodeConfig("3.11.0")
176+
177+
// then
178+
expect(result.success).toBe(true)
179+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
180+
expect(savedConfig.plugin).toContain("oh-my-openagent")
181+
})
182+
183+
it("adds plugin when plugin array is empty", async () => {
184+
// given - empty plugin array
185+
const config = { plugin: [] }
186+
writeFileSync(testConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8")
187+
188+
// when
189+
const result = await addPluginToOpenCodeConfig("3.11.0")
190+
191+
// then
192+
expect(result.success).toBe(true)
193+
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
194+
expect(savedConfig.plugin).toContain("oh-my-openagent")
195+
})
196+
})
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
11
import { fetchNpmDistTags } from "./npm-dist-tags"
22

3-
const PACKAGE_NAME = "oh-my-opencode"
3+
const DEFAULT_PACKAGE_NAME = "oh-my-opencode"
4+
const NEW_PACKAGE_NAME = "oh-my-openagent"
45
const PRIORITIZED_TAGS = ["latest", "beta", "next"] as const
56

6-
function getFallbackEntry(version: string): string {
7+
function getFallbackEntry(version: string, packageName: string): string {
78
const prereleaseMatch = version.match(/-([a-zA-Z][a-zA-Z0-9-]*)(?:\.|$)/)
89
if (prereleaseMatch) {
9-
return `${PACKAGE_NAME}@${prereleaseMatch[1]}`
10+
return `${packageName}@${prereleaseMatch[1]}`
1011
}
1112

12-
return PACKAGE_NAME
13+
return packageName
1314
}
1415

15-
export async function getPluginNameWithVersion(currentVersion: string): Promise<string> {
16-
const distTags = await fetchNpmDistTags(PACKAGE_NAME)
16+
export async function getPluginNameWithVersion(
17+
currentVersion: string,
18+
packageName: string = DEFAULT_PACKAGE_NAME
19+
): Promise<string> {
20+
const distTags = await fetchNpmDistTags(NEW_PACKAGE_NAME)
21+
1722

1823
if (distTags) {
1924
const allTags = new Set([...PRIORITIZED_TAGS, ...Object.keys(distTags)])
2025
for (const tag of allTags) {
2126
if (distTags[tag] === currentVersion) {
22-
return `${PACKAGE_NAME}@${tag}`
27+
return `${packageName}@${tag}`
2328
}
2429
}
2530
}
2631

27-
return getFallbackEntry(currentVersion)
32+
return getFallbackEntry(currentVersion, packageName)
2833
}

0 commit comments

Comments
 (0)