refactor: rename remaining oh-my-opencode string literals

This commit is contained in:
YeonGyu-Kim
2026-03-17 13:56:15 +09:00
parent ffa3d43ccb
commit 8efbda9332
49 changed files with 218 additions and 199 deletions
+4 -4
View File
@@ -32,7 +32,7 @@ function supportsAvx2() {
return null;
}
if (process.env.OH_MY_OPENCODE_FORCE_BASELINE === "1") {
if (process.env.OH_MY_OPENAGENT_FORCE_BASELINE === "1" || process.env.OH_MY_OPENCODE_FORCE_BASELINE === "1") {
return false;
}
@@ -85,7 +85,7 @@ function main() {
preferBaseline: avx2Supported === false,
});
} catch (error) {
console.error(`\noh-my-opencode: ${error.message}\n`);
console.error(`\noh-my-openagent: ${error.message}\n`);
process.exit(1);
}
@@ -100,7 +100,7 @@ function main() {
.filter((entry) => entry !== null);
if (resolvedBinaries.length === 0) {
console.error(`\noh-my-opencode: Platform binary not installed.`);
console.error(`\noh-my-openagent: Platform binary not installed.`);
console.error(`\nYour platform: ${platform}-${arch}${libcFamily === "musl" ? "-musl" : ""}`);
console.error(`Expected packages (in order): ${packageCandidates.join(", ")}`);
console.error(`\nTo fix, run:`);
@@ -120,7 +120,7 @@ function main() {
continue;
}
console.error(`\noh-my-opencode: Failed to execute binary.`);
console.error(`\noh-my-openagent: Failed to execute binary.`);
console.error(`Error: ${result.error.message}\n`);
process.exit(2);
}
+2 -2
View File
@@ -48,9 +48,9 @@ function main() {
);
}
console.log(`✓ oh-my-opencode binary installed for ${platform}-${arch} (${resolvedPackage})`);
console.log(`✓ oh-my-openagent binary installed for ${platform}-${arch} (${resolvedPackage})`)
} catch (error) {
console.warn(`⚠ oh-my-opencode: ${error.message}`);
console.warn(`⚠ oh-my-openagent: ${error.message}`)
console.warn(` The CLI may not work on this platform.`);
// Don't fail installation - let user try anyway
}
@@ -2,7 +2,7 @@
exports[`generateModelConfig no providers available returns ULTIMATE_FALLBACK for all agents and categories when no providers 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/gpt-5-nano",
@@ -66,7 +66,7 @@ exports[`generateModelConfig no providers available returns ULTIMATE_FALLBACK fo
exports[`generateModelConfig single native provider uses Claude models when only Claude is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -128,7 +128,7 @@ exports[`generateModelConfig single native provider uses Claude models when only
exports[`generateModelConfig single native provider uses Claude models with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -191,7 +191,7 @@ exports[`generateModelConfig single native provider uses Claude models with isMa
exports[`generateModelConfig single native provider uses OpenAI models when only OpenAI is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "openai/gpt-5.4",
@@ -277,7 +277,7 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
exports[`generateModelConfig single native provider uses OpenAI models with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "openai/gpt-5.4",
@@ -363,7 +363,7 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
exports[`generateModelConfig single native provider uses Gemini models when only Gemini is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/gpt-5-nano",
@@ -423,7 +423,7 @@ exports[`generateModelConfig single native provider uses Gemini models when only
exports[`generateModelConfig single native provider uses Gemini models with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/gpt-5-nano",
@@ -483,7 +483,7 @@ exports[`generateModelConfig single native provider uses Gemini models with isMa
exports[`generateModelConfig all native providers uses preferred models from fallback chains when all natives available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -558,7 +558,7 @@ exports[`generateModelConfig all native providers uses preferred models from fal
exports[`generateModelConfig all native providers uses preferred models with isMax20 flag when all natives available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -634,7 +634,7 @@ exports[`generateModelConfig all native providers uses preferred models with isM
exports[`generateModelConfig fallback providers uses OpenCode Zen models when only OpenCode Zen is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/claude-sonnet-4-6",
@@ -709,7 +709,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
exports[`generateModelConfig fallback providers uses OpenCode Zen models with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/claude-sonnet-4-6",
@@ -785,7 +785,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
exports[`generateModelConfig fallback providers uses GitHub Copilot models when only Copilot is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.6",
@@ -855,7 +855,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models when
exports[`generateModelConfig fallback providers uses GitHub Copilot models with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.6",
@@ -926,7 +926,7 @@ exports[`generateModelConfig fallback providers uses GitHub Copilot models with
exports[`generateModelConfig fallback providers uses ZAI model for librarian when only ZAI is available 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/gpt-5-nano",
@@ -984,7 +984,7 @@ exports[`generateModelConfig fallback providers uses ZAI model for librarian whe
exports[`generateModelConfig fallback providers uses ZAI model for librarian with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "opencode/gpt-5-nano",
@@ -1042,7 +1042,7 @@ exports[`generateModelConfig fallback providers uses ZAI model for librarian wit
exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen combination 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -1117,7 +1117,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot combination 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.6",
@@ -1192,7 +1192,7 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combination (librarian uses ZAI) 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -1256,7 +1256,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + ZAI combinat
exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combination (explore uses Gemini) 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -1322,7 +1322,7 @@ exports[`generateModelConfig mixed provider scenarios uses Gemini + Claude combi
exports[`generateModelConfig mixed provider scenarios uses all fallback providers together 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "github-copilot/claude-sonnet-4.6",
@@ -1400,7 +1400,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
exports[`generateModelConfig mixed provider scenarios uses all providers together 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
@@ -1478,7 +1478,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
exports[`generateModelConfig mixed provider scenarios uses all providers with isMax20 flag 1`] = `
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json",
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json",
"agents": {
"atlas": {
"model": "anthropic/claude-sonnet-4-6",
+1 -1
View File
@@ -43,7 +43,7 @@ describe("runCliInstaller", () => {
}),
spyOn(configManager, "writeOmoConfig").mockReturnValue({
success: true,
configPath: "/tmp/oh-my-opencode.jsonc",
configPath: "/tmp/oh-my-openagent.jsonc"
}),
]
+3 -3
View File
@@ -32,7 +32,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi
}
console.log()
printInfo(
"Usage: bunx oh-my-opencode install --no-tui --claude=<no|yes|max20> --gemini=<no|yes> --copilot=<no|yes>",
"Usage: bunx oh-my-openagent install --no-tui --claude=<no|yes|max20> --gemini=<no|yes> --copilot=<no|yes>"
)
console.log()
return 1
@@ -65,7 +65,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi
const config = argsToConfig(args)
printStep(step++, totalSteps, "Adding oh-my-opencode plugin...")
printStep(step++, totalSteps, "Adding oh-my-openagent plugin...")
const pluginResult = await addPluginToOpenCodeConfig(version)
if (!pluginResult.success) {
printError(`Failed: ${pluginResult.error}`)
@@ -75,7 +75,7 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi
`Plugin ${isUpdate ? "verified" : "added"} ${SYMBOLS.arrow} ${color.dim(pluginResult.configPath)}`,
)
printStep(step++, totalSteps, "Writing oh-my-opencode configuration...")
printStep(step++, totalSteps, "Writing oh-my-openagent configuration...")
const omoResult = writeOmoConfig(config)
if (!omoResult.success) {
printError(`Failed: ${omoResult.error}`)
+24 -24
View File
@@ -15,14 +15,14 @@ const VERSION = packageJson.version
const program = new Command()
program
.name("oh-my-opencode")
.name("oh-my-openagent")
.description("The ultimate OpenCode plugin - multi-model orchestration, LSP tools, and more")
.version(VERSION, "-v, --version", "Show version number")
.enablePositionalOptions()
program
.command("install")
.description("Install and configure oh-my-opencode with interactive setup")
.description("Install and configure oh-my-openagent with interactive setup")
.option("--no-tui", "Run in non-interactive mode (requires all options)")
.option("--claude <value>", "Claude subscription: no, yes, max20")
.option("--openai <value>", "OpenAI/ChatGPT subscription: no, yes (default: no)")
@@ -35,9 +35,9 @@ program
.option("--skip-auth", "Skip authentication setup hints")
.addHelpText("after", `
Examples:
$ bunx oh-my-opencode install
$ bunx oh-my-opencode install --no-tui --claude=max20 --openai=yes --gemini=yes --copilot=no
$ bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=yes --opencode-zen=yes
$ bunx oh-my-openagent install
$ bunx oh-my-openagent install --no-tui --claude=max20 --openai=yes --gemini=yes --copilot=no
$ bunx oh-my-openagent install --no-tui --claude=no --gemini=no --copilot=yes --opencode-zen=yes
Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi):
Claude Native anthropic/ models (Opus, Sonnet, Haiku)
@@ -82,20 +82,20 @@ program
.option("--session-id <id>", "Resume existing session instead of creating new one")
.addHelpText("after", `
Examples:
$ bunx oh-my-opencode run "Fix the bug in index.ts"
$ bunx oh-my-opencode run --agent Sisyphus "Implement feature X"
$ bunx oh-my-opencode run --port 4321 "Fix the bug"
$ bunx oh-my-opencode run --attach http://127.0.0.1:4321 "Fix the bug"
$ bunx oh-my-opencode run --json "Fix the bug" | jq .sessionId
$ bunx oh-my-opencode run --on-complete "notify-send Done" "Fix the bug"
$ bunx oh-my-opencode run --session-id ses_abc123 "Continue the work"
$ bunx oh-my-opencode run --model anthropic/claude-sonnet-4 "Fix the bug"
$ bunx oh-my-opencode run --agent Sisyphus --model openai/gpt-5.4 "Implement feature X"
$ bunx oh-my-openagent run "Fix the bug in index.ts"
$ bunx oh-my-openagent run --agent Sisyphus "Implement feature X"
$ bunx oh-my-openagent run --port 4321 "Fix the bug"
$ bunx oh-my-openagent run --attach http://127.0.0.1:4321 "Fix the bug"
$ bunx oh-my-openagent run --json "Fix the bug" | jq .sessionId
$ bunx oh-my-openagent run --on-complete "notify-send Done" "Fix the bug"
$ bunx oh-my-openagent run --session-id ses_abc123 "Continue the work"
$ bunx oh-my-openagent run --model anthropic/claude-sonnet-4 "Fix the bug"
$ bunx oh-my-openagent run --agent Sisyphus --model openai/gpt-5.4 "Implement feature X"
Agent resolution order:
1) --agent flag
2) OPENCODE_DEFAULT_AGENT
3) oh-my-opencode.json "default_run_agent"
3) oh-my-openagent.json "default_run_agent"
4) Sisyphus (fallback)
Available core agents:
@@ -134,9 +134,9 @@ program
.option("--json", "Output in JSON format for scripting")
.addHelpText("after", `
Examples:
$ bunx oh-my-opencode get-local-version
$ bunx oh-my-opencode get-local-version --json
$ bunx oh-my-opencode get-local-version --directory /path/to/project
$ bunx oh-my-openagent get-local-version
$ bunx oh-my-openagent get-local-version --json
$ bunx oh-my-openagent get-local-version --directory /path/to/project
This command shows:
- Current installed version
@@ -155,16 +155,16 @@ This command shows:
program
.command("doctor")
.description("Check oh-my-opencode installation health and diagnose issues")
.description("Check oh-my-openagent installation health and diagnose issues")
.option("--status", "Show compact system dashboard")
.option("--verbose", "Show detailed diagnostic information")
.option("--json", "Output results in JSON format")
.addHelpText("after", `
Examples:
$ bunx oh-my-opencode doctor # Show problems only
$ bunx oh-my-opencode doctor --status # Compact dashboard
$ bunx oh-my-opencode doctor --verbose # Deep diagnostics
$ bunx oh-my-opencode doctor --json # JSON output
$ bunx oh-my-openagent doctor # Show problems only
$ bunx oh-my-openagent doctor --status # Compact dashboard
$ bunx oh-my-openagent doctor --verbose # Deep diagnostics
$ bunx oh-my-openagent doctor --json # JSON output
`)
.action(async (options) => {
const mode = options.status ? "status" : options.verbose ? "verbose" : "default"
@@ -180,7 +180,7 @@ program
.command("version")
.description("Show version information")
.action(() => {
console.log(`oh-my-opencode v${VERSION}`)
console.log(`oh-my-openagent v${VERSION}`)
})
program.addCommand(createMcpOAuthCommand())
+8 -8
View File
@@ -23,7 +23,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("2.14.0")
// #then should use @latest tag
expect(result).toBe("oh-my-opencode@latest")
expect(result).toBe("oh-my-openagent@latest")
})
test("returns @beta when current version matches beta tag", async () => {
@@ -39,7 +39,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("3.0.0-beta.3")
// #then should use @beta tag
expect(result).toBe("oh-my-opencode@beta")
expect(result).toBe("oh-my-openagent@beta")
})
test("returns @next when current version matches next tag", async () => {
@@ -55,7 +55,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("3.1.0-next.1")
// #then should use @next tag
expect(result).toBe("oh-my-opencode@next")
expect(result).toBe("oh-my-openagent@next")
})
test("returns prerelease channel tag when no dist-tag matches prerelease version", async () => {
@@ -71,7 +71,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("3.0.0-beta.2")
// #then should preserve prerelease channel
expect(result).toBe("oh-my-opencode@beta")
expect(result).toBe("oh-my-openagent@beta")
})
test("returns prerelease channel tag when fetch fails", async () => {
@@ -82,7 +82,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("3.0.0-beta.3")
// #then should preserve prerelease channel
expect(result).toBe("oh-my-opencode@beta")
expect(result).toBe("oh-my-openagent@beta")
})
test("returns bare package name when npm returns non-ok response for stable version", async () => {
@@ -98,7 +98,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("2.14.0")
// #then should fall back to bare package entry
expect(result).toBe("oh-my-opencode")
expect(result).toBe("oh-my-openagent")
})
test("prioritizes latest over other tags when version matches multiple", async () => {
@@ -114,7 +114,7 @@ describe("getPluginNameWithVersion", () => {
const result = await getPluginNameWithVersion("3.0.0")
// #then should prioritize @latest
expect(result).toBe("oh-my-opencode@latest")
expect(result).toBe("oh-my-openagent@latest")
})
})
@@ -207,7 +207,7 @@ describe("generateOmoConfig - model fallback system", () => {
const result = generateOmoConfig(config)
// #then Sisyphus is omitted (requires all fallback providers)
expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json")
expect(result.$schema).toBe("https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json")
expect((result.agents as Record<string, { model: string }>).sisyphus).toBeUndefined()
})
@@ -34,7 +34,7 @@ describe("writeOmoConfig", () => {
beforeEach(() => {
testConfigDir = join(tmpdir(), `omo-write-config-${Date.now()}-${Math.random().toString(36).slice(2)}`)
testConfigPath = join(testConfigDir, "oh-my-opencode.json")
testConfigPath = join(testConfigDir, "oh-my-openagent.json")
mkdirSync(testConfigDir, { recursive: true })
process.env.OPENCODE_CONFIG_DIR = testConfigDir
+1 -1
View File
@@ -61,7 +61,7 @@ export function writeOmoConfig(installConfig: InstallConfig): ConfigMergeResult
return {
success: false,
configPath: omoConfigPath,
error: formatErrorWithSuggestion(err, "write oh-my-opencode config"),
error: formatErrorWithSuggestion(err, "write oh-my-openagent config")
}
}
}
@@ -32,7 +32,7 @@ describe("model-resolution check", () => {
})
describe("getModelResolutionInfoWithOverrides", () => {
// given: User has overrides in oh-my-opencode.json
// given: User has overrides in oh-my-openagent.json
// when: Getting resolution info with config
// then: Shows user override in Step 1 position
+6 -6
View File
@@ -5,7 +5,7 @@ const mockGetOpenCodeVersion = mock(async () => "1.0.200")
const mockCompareVersions = mock(() => true)
const mockGetPluginInfo = mock(() => ({
registered: true,
entry: "oh-my-opencode",
entry: "oh-my-openagent",
isPinned: false,
pinnedVersion: null,
configPath: null,
@@ -14,7 +14,7 @@ const mockGetPluginInfo = mock(() => ({
const mockGetLoadedPluginVersion = mock(() => ({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-openagent/package.json",
expectedVersion: "3.0.0",
loadedVersion: "3.1.0",
}))
@@ -51,7 +51,7 @@ describe("system check", () => {
mockCompareVersions.mockReturnValue(true)
mockGetPluginInfo.mockReturnValue({
registered: true,
entry: "oh-my-opencode",
entry: "oh-my-openagent",
isPinned: false,
pinnedVersion: null,
configPath: null,
@@ -60,7 +60,7 @@ describe("system check", () => {
mockGetLoadedPluginVersion.mockReturnValue({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-openagent/package.json",
expectedVersion: "3.0.0",
loadedVersion: "3.1.0",
})
@@ -82,7 +82,7 @@ describe("system check", () => {
mockGetLoadedPluginVersion.mockReturnValue({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-openagent/package.json",
expectedVersion: "3.0.0-canary.1",
loadedVersion: "3.0.0-canary.1",
})
@@ -97,7 +97,7 @@ describe("system check", () => {
//#then
const outdatedIssue = result.issues.find((issue) => issue.title === "Loaded plugin is outdated")
expect(outdatedIssue?.fix).toBe(
'Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add oh-my-opencode@canary'
'Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add oh-my-openagent@canary'
)
})
})
+3 -3
View File
@@ -82,9 +82,9 @@ export async function checkSystem(): Promise<CheckResult> {
if (!pluginInfo.registered) {
issues.push({
title: "oh-my-opencode is not registered",
title: "oh-my-openagent is not registered",
description: "Plugin entry is missing from OpenCode configuration.",
fix: "Run: bunx oh-my-opencode install",
fix: "Run: bunx oh-my-openagent install",
severity: "error",
affects: ["all agents"],
})
@@ -108,7 +108,7 @@ export async function checkSystem(): Promise<CheckResult> {
issues.push({
title: "Loaded plugin is outdated",
description: `Loaded ${systemInfo.loadedVersion}, latest ${latestVersion}.`,
fix: `Update: cd "${loadedInfo.cacheDir}" && bun add oh-my-opencode@${installTag}`,
fix: `Update: cd "${loadedInfo.cacheDir}" && bun add oh-my-openagent@${installTag}`,
severity: "warning",
affects: ["plugin features"],
})
+1 -1
View File
@@ -15,7 +15,7 @@ export function formatDefault(result: DoctorResult): string {
const pluginVer = result.systemInfo.pluginVersion ?? "unknown"
lines.push(
` ${color.green(SYMBOLS.check)} ${color.green(
`System OK (opencode ${opencodeVer} · oh-my-opencode ${pluginVer})`
`System OK (opencode ${opencodeVer} · oh-my-openagent ${pluginVer})`
)}`
)
} else {
+1 -1
View File
@@ -12,7 +12,7 @@ export function formatVerbose(result: DoctorResult): string {
lines.push(`${color.bold("System Information")}`)
lines.push(`${color.dim("\u2500".repeat(40))}`)
lines.push(` ${formatStatusSymbol("pass")} opencode ${systemInfo.opencodeVersion ?? "unknown"}`)
lines.push(` ${formatStatusSymbol("pass")} oh-my-opencode ${systemInfo.pluginVersion ?? "unknown"}`)
lines.push(` ${formatStatusSymbol("pass")} oh-my-openagent ${systemInfo.pluginVersion ?? "unknown"}`)
if (systemInfo.loadedVersion) {
lines.push(` ${formatStatusSymbol("pass")} loaded ${systemInfo.loadedVersion}`)
}
+1 -1
View File
@@ -81,7 +81,7 @@ describe("formatDoctorOutput", () => {
const output = stripAnsi(formatDoctorOutput(result, "default"))
//#then
expect(output).toContain("System OK (opencode 1.0.200 · oh-my-opencode 3.4.0)")
expect(output).toContain("System OK (opencode 1.0.200 · oh-my-openagent 3.4.0)")
})
it("shows issue count and details when issues exist", async () => {
+2 -2
View File
@@ -15,7 +15,7 @@ export function formatVersionOutput(info: VersionInfo): string {
const lines: string[] = []
lines.push("")
lines.push(color.bold(color.white("oh-my-opencode Version Information")))
lines.push(color.bold(color.white("oh-my-openagent Version Information")))
lines.push(color.dim("─".repeat(50)))
lines.push("")
@@ -37,7 +37,7 @@ export function formatVersionOutput(info: VersionInfo): string {
break
case "outdated":
lines.push(` ${SYMBOLS.warn} ${color.yellow("Update available")}`)
lines.push(` ${color.dim("Run:")} ${color.cyan("cd ~/.config/opencode && bun update oh-my-opencode")}`)
lines.push(` ${color.dim("Run:")} ${color.cyan("cd ~/.config/opencode && bun update oh-my-openagent")}`)
break
case "local-dev":
lines.push(` ${SYMBOLS.dev} ${color.cyan("Running in local development mode")}`)
+1 -1
View File
@@ -108,7 +108,7 @@ describe("install CLI - binary check behavior", () => {
// then opencode.json should have plugin entry
const config = JSON.parse(readFileSync(configPath, "utf-8"))
expect(config.plugin).toBeDefined()
expect(config.plugin.some((p: string) => p.includes("oh-my-opencode"))).toBe(true)
expect(config.plugin.some((p: string) => p.includes("oh-my-openagent"))).toBe(true)
// then exit code should be 0 (success)
expect(exitCode).toBe(0)
+1 -1
View File
@@ -559,7 +559,7 @@ describe("generateModelConfig", () => {
// #then should include correct schema URL
expect(result.$schema).toBe(
"https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json"
"https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json"
)
})
})
+1 -1
View File
@@ -20,7 +20,7 @@ export type { GeneratedOmoConfig } from "./model-fallback-types"
const ZAI_MODEL = "zai-coding-plan/glm-4.7"
const ULTIMATE_FALLBACK = "opencode/gpt-5-nano"
const SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json"
const SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json"
+2 -2
View File
@@ -86,7 +86,7 @@ describe("resolveSession", () => {
expect(result).toBe("new-session-id")
expect(mockClient.session.create).toHaveBeenCalledWith({
body: {
title: "oh-my-opencode run",
title: "oh-my-openagent run",
permission: [
{ permission: "question", action: "deny", pattern: "*" },
],
@@ -113,7 +113,7 @@ describe("resolveSession", () => {
expect(mockClient.session.create).toHaveBeenCalledTimes(2)
expect(mockClient.session.create).toHaveBeenCalledWith({
body: {
title: "oh-my-opencode run",
title: "oh-my-openagent run",
permission: [
{ permission: "question", action: "deny", pattern: "*" },
],
+1 -1
View File
@@ -26,7 +26,7 @@ export async function resolveSession(options: {
for (let attempt = 1; attempt <= SESSION_CREATE_MAX_RETRIES; attempt++) {
const res = await client.session.create({
body: {
title: "oh-my-opencode run",
title: "oh-my-openagent run",
// In CLI run mode there's no TUI to answer questions.
permission: [
{ permission: "question", action: "deny" as const, pattern: "*" },
+2 -2
View File
@@ -43,7 +43,7 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi
const config = await promptInstallConfig(detected)
if (!config) return 1
spinner.start("Adding oh-my-opencode to OpenCode config")
spinner.start("Adding oh-my-openagent to OpenCode config")
const pluginResult = await addPluginToOpenCodeConfig(version)
if (!pluginResult.success) {
spinner.stop(`Failed to add plugin: ${pluginResult.error}`)
@@ -52,7 +52,7 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi
}
spinner.stop(`Plugin added to ${color.cyan(pluginResult.configPath)}`)
spinner.start("Writing oh-my-opencode configuration")
spinner.start("Writing oh-my-openagent configuration")
const omoResult = writeOmoConfig(config)
if (!omoResult.success) {
spinner.stop(`Failed to write config: ${omoResult.error}`)
+1 -1
View File
@@ -25,7 +25,7 @@ export const OhMyOpenAgentConfigSchema = z.object({
$schema: z.string().optional(),
/** Enable new task system (default: false) */
new_task_system_enabled: z.boolean().optional(),
/** Default agent name for `oh-my-opencode run` (env: OPENCODE_DEFAULT_AGENT) */
/** Default agent name for `oh-my-openagent run` (env: OPENCODE_DEFAULT_AGENT) */
default_run_agent: z.string().optional(),
disabled_mcps: z.array(AnyMcpNameSchema).optional(),
disabled_agents: z.array(z.string()).optional(),
@@ -222,11 +222,12 @@ export interface ClaudeSettings {
*/
export interface PluginLoaderOptions {
/**
* Override enabled plugins from oh-my-opencode config.
* Override enabled plugins from oh-my-openagent config.
* Key format: "pluginName@marketplace" (e.g., "shell-scripting@claude-code-workflows")
* Value: true = enabled, false = disabled
*
*
* This takes precedence over ~/.claude/settings.json enabledPlugins
*/
enabledPluginsOverride?: Record<string, boolean>
}
+11 -11
View File
@@ -9,7 +9,7 @@ const TEST_USER_CONFIG_DIR = "/tmp/opencode-config"
mock.module("./constants", () => ({
CACHE_DIR: TEST_OPENCODE_CACHE_DIR,
USER_CONFIG_DIR: TEST_USER_CONFIG_DIR,
PACKAGE_NAME: "oh-my-opencode",
PACKAGE_NAME: "oh-my-openagent",
}))
mock.module("../../shared/logger", () => ({
@@ -21,10 +21,10 @@ function resetTestCache(): void {
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
}
mkdirSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode"), { recursive: true })
mkdirSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-openagent"), { recursive: true })
writeFileSync(
join(TEST_OPENCODE_CACHE_DIR, "package.json"),
JSON.stringify({ dependencies: { "oh-my-opencode": "latest", other: "1.0.0" } }, null, 2)
JSON.stringify({ dependencies: { "oh-my-openagent": "latest", other: "1.0.0" } }, null, 2)
)
writeFileSync(
join(TEST_OPENCODE_CACHE_DIR, "bun.lock"),
@@ -32,11 +32,11 @@ function resetTestCache(): void {
{
workspaces: {
"": {
dependencies: { "oh-my-opencode": "latest", other: "1.0.0" },
dependencies: { "oh-my-openagent": "latest", other: "1.0.0" },
},
},
packages: {
"oh-my-opencode": {},
"oh-my-openagent": {},
other: {},
},
},
@@ -45,8 +45,8 @@ function resetTestCache(): void {
)
)
writeFileSync(
join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode", "package.json"),
'{"name":"oh-my-opencode"}'
join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-openagent", "package.json"),
'{"name":"oh-my-openagent"}'
)
}
@@ -67,21 +67,21 @@ describe("invalidatePackage", () => {
const result = invalidatePackage()
expect(result).toBe(true)
expect(existsSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode"))).toBe(false)
expect(existsSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-openagent"))).toBe(false)
const packageJson = JSON.parse(readFileSync(join(TEST_OPENCODE_CACHE_DIR, "package.json"), "utf-8")) as {
dependencies?: Record<string, string>
}
expect(packageJson.dependencies?.["oh-my-opencode"]).toBe("latest")
expect(packageJson.dependencies?.["oh-my-openagent"]).toBe("latest")
expect(packageJson.dependencies?.other).toBe("1.0.0")
const bunLock = JSON.parse(readFileSync(join(TEST_OPENCODE_CACHE_DIR, "bun.lock"), "utf-8")) as {
workspaces?: { ""?: { dependencies?: Record<string, string> } }
packages?: Record<string, unknown>
}
expect(bunLock.workspaces?.[""]?.dependencies?.["oh-my-opencode"]).toBe("latest")
expect(bunLock.workspaces?.[""]?.dependencies?.["oh-my-openagent"]).toBe("latest")
expect(bunLock.workspaces?.[""]?.dependencies?.other).toBe("1.0.0")
expect(bunLock.packages?.["oh-my-opencode"]).toBeUndefined()
expect(bunLock.packages?.["oh-my-openagent"]).toBeUndefined()
expect(bunLock.packages?.other).toEqual({})
})
})
@@ -21,18 +21,18 @@ describe("pinned-version-updater", () => {
test("updates pinned version in config", () => {
//#given
const config = JSON.stringify({
plugin: ["oh-my-opencode@3.1.8"],
plugin: ["oh-my-openagent@3.1.8"],
})
fs.writeFileSync(configPath, config)
//#when
const result = updatePinnedVersion(configPath, "oh-my-opencode@3.1.8", "3.4.0")
const result = updatePinnedVersion(configPath, "oh-my-openagent@3.1.8", "3.4.0")
//#then
expect(result).toBe(true)
const updated = fs.readFileSync(configPath, "utf-8")
expect(updated).toContain("oh-my-opencode@3.4.0")
expect(updated).not.toContain("oh-my-opencode@3.1.8")
expect(updated).toContain("oh-my-openagent@3.4.0")
expect(updated).not.toContain("oh-my-openagent@3.1.8")
})
test("returns false when entry not found", () => {
@@ -43,7 +43,7 @@ describe("pinned-version-updater", () => {
fs.writeFileSync(configPath, config)
//#when
const result = updatePinnedVersion(configPath, "oh-my-opencode@3.1.8", "3.4.0")
const result = updatePinnedVersion(configPath, "oh-my-openagent@3.1.8", "3.4.0")
//#then
expect(result).toBe(false)
@@ -55,7 +55,7 @@ describe("pinned-version-updater", () => {
fs.writeFileSync(configPath, config)
//#when
const result = updatePinnedVersion(configPath, "oh-my-opencode@3.1.8", "3.4.0")
const result = updatePinnedVersion(configPath, "oh-my-openagent@3.1.8", "3.4.0")
//#then
expect(result).toBe(false)
@@ -66,46 +66,46 @@ describe("pinned-version-updater", () => {
test("reverts from failed version back to original entry", () => {
//#given
const config = JSON.stringify({
plugin: ["oh-my-opencode@3.4.0"],
plugin: ["oh-my-openagent@3.4.0"],
})
fs.writeFileSync(configPath, config)
//#when
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-opencode@3.1.8")
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-openagent@3.1.8")
//#then
expect(result).toBe(true)
const reverted = fs.readFileSync(configPath, "utf-8")
expect(reverted).toContain("oh-my-opencode@3.1.8")
expect(reverted).not.toContain("oh-my-opencode@3.4.0")
expect(reverted).toContain("oh-my-openagent@3.1.8")
expect(reverted).not.toContain("oh-my-openagent@3.4.0")
})
test("reverts to unpinned entry", () => {
//#given
const config = JSON.stringify({
plugin: ["oh-my-opencode@3.4.0"],
plugin: ["oh-my-openagent@3.4.0"],
})
fs.writeFileSync(configPath, config)
//#when
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-opencode")
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-openagent")
//#then
expect(result).toBe(true)
const reverted = fs.readFileSync(configPath, "utf-8")
expect(reverted).toContain('"oh-my-opencode"')
expect(reverted).not.toContain("oh-my-opencode@3.4.0")
expect(reverted).toContain('"oh-my-openagent"')
expect(reverted).not.toContain("oh-my-openagent@3.4.0")
})
test("returns false when failed version not found", () => {
//#given
const config = JSON.stringify({
plugin: ["oh-my-opencode@3.1.8"],
plugin: ["oh-my-openagent@3.1.8"],
})
fs.writeFileSync(configPath, config)
//#when
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-opencode@3.1.8")
const result = revertPinnedVersion(configPath, "3.4.0", "oh-my-openagent@3.1.8")
//#then
expect(result).toBe(false)
@@ -116,18 +116,18 @@ describe("pinned-version-updater", () => {
test("config returns to original state after update + revert", () => {
//#given
const originalConfig = JSON.stringify({
plugin: ["oh-my-opencode@3.1.8"],
plugin: ["oh-my-openagent@3.1.8"],
})
fs.writeFileSync(configPath, originalConfig)
//#when
updatePinnedVersion(configPath, "oh-my-opencode@3.1.8", "3.4.0")
revertPinnedVersion(configPath, "3.4.0", "oh-my-opencode@3.1.8")
updatePinnedVersion(configPath, "oh-my-openagent@3.1.8", "3.4.0")
revertPinnedVersion(configPath, "3.4.0", "oh-my-openagent@3.1.8")
//#then
const finalConfig = fs.readFileSync(configPath, "utf-8")
expect(finalConfig).toContain("oh-my-opencode@3.1.8")
expect(finalConfig).not.toContain("oh-my-opencode@3.4.0")
expect(finalConfig).toContain("oh-my-openagent@3.1.8")
expect(finalConfig).not.toContain("oh-my-openagent@3.4.0")
})
})
})
@@ -21,7 +21,7 @@ describe("findPluginEntry", () => {
test("returns unpinned for bare package name", () => {
// #given plugin is configured without a tag
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-opencode"] }))
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-openagent"] }))
// #when plugin entry is detected
const pluginInfo = findPluginEntry(temporaryDirectory)
@@ -34,7 +34,7 @@ describe("findPluginEntry", () => {
test("returns unpinned for latest dist-tag", () => {
// #given plugin is configured with latest dist-tag
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-opencode@latest"] }))
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-openagent@latest"] }))
// #when plugin entry is detected
const pluginInfo = findPluginEntry(temporaryDirectory)
@@ -47,7 +47,7 @@ describe("findPluginEntry", () => {
test("returns unpinned for beta dist-tag", () => {
// #given plugin is configured with beta dist-tag
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-opencode@beta"] }))
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-openagent@beta"] }))
// #when plugin entry is detected
const pluginInfo = findPluginEntry(temporaryDirectory)
@@ -60,7 +60,7 @@ describe("findPluginEntry", () => {
test("returns pinned for explicit semver", () => {
// #given plugin is configured with explicit version
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-opencode@3.5.2"] }))
fs.writeFileSync(configPath, JSON.stringify({ plugin: ["oh-my-openagent@3.5.2"] }))
// #when plugin entry is detected
const pluginInfo = findPluginEntry(temporaryDirectory)
@@ -7,14 +7,14 @@ const TEST_CACHE_DIR = join(import.meta.dir, "__test-sync-cache__")
mock.module("../constants", () => ({
CACHE_DIR: TEST_CACHE_DIR,
PACKAGE_NAME: "oh-my-opencode",
NPM_REGISTRY_URL: "https://registry.npmjs.org/-/package/oh-my-opencode/dist-tags",
PACKAGE_NAME: "oh-my-openagent",
NPM_REGISTRY_URL: "https://registry.npmjs.org/-/package/oh-my-openagent/dist-tags",
NPM_FETCH_TIMEOUT: 5000,
VERSION_FILE: join(TEST_CACHE_DIR, "version"),
USER_CONFIG_DIR: "/tmp/opencode-config",
USER_OPENCODE_CONFIG: "/tmp/opencode-config/opencode.json",
USER_OPENCODE_CONFIG_JSONC: "/tmp/opencode-config/opencode.jsonc",
INSTALLED_PACKAGE_JSON: join(TEST_CACHE_DIR, "node_modules", "oh-my-opencode", "package.json"),
INSTALLED_PACKAGE_JSON: join(TEST_CACHE_DIR, "node_modules", "oh-my-openagent", "package.json"),
getWindowsAppdataDir: () => null,
}))
@@ -30,7 +30,7 @@ function resetTestCache(currentVersion = "3.10.0"): void {
mkdirSync(TEST_CACHE_DIR, { recursive: true })
writeFileSync(
join(TEST_CACHE_DIR, "package.json"),
JSON.stringify({ dependencies: { "oh-my-opencode": currentVersion, other: "1.0.0" } }, null, 2)
JSON.stringify({ dependencies: { "oh-my-openagent": currentVersion, other: "1.0.0" } }, null, 2)
)
}
@@ -43,7 +43,7 @@ function cleanupTestCache(): void {
function readCachePackageJsonVersion(): string | undefined {
const content = readFileSync(join(TEST_CACHE_DIR, "package.json"), "utf-8")
const pkg = JSON.parse(content) as { dependencies?: Record<string, string> }
return pkg.dependencies?.["oh-my-opencode"]
return pkg.dependencies?.["oh-my-openagent"]
}
describe("syncCachePackageJsonToIntent", () => {
@@ -61,7 +61,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -80,7 +80,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@next",
entry: "oh-my-openagent@next",
isPinned: false,
pinnedVersion: "next",
configPath: "/tmp/opencode.json",
@@ -99,7 +99,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode",
entry: "oh-my-openagent",
isPinned: false,
pinnedVersion: null,
configPath: "/tmp/opencode.json",
@@ -120,7 +120,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -140,7 +140,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -165,7 +165,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -184,7 +184,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@3.10.0",
entry: "oh-my-openagent@3.10.0",
isPinned: true,
pinnedVersion: "3.10.0",
configPath: "/tmp/opencode.json",
@@ -203,7 +203,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -229,7 +229,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -248,7 +248,7 @@ describe("syncCachePackageJsonToIntent", () => {
mkdirSync(TEST_CACHE_DIR, { recursive: true })
writeFileSync(
join(TEST_CACHE_DIR, "package.json"),
JSON.stringify({ dependencies: { "oh-my-opencode": "3.10.0" } }, null, 2)
JSON.stringify({ dependencies: { "oh-my-openagent": "3.10.0" } }, null, 2)
)
const fs = await import("node:fs")
@@ -267,7 +267,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
@@ -293,7 +293,7 @@ describe("syncCachePackageJsonToIntent", () => {
mkdirSync(TEST_CACHE_DIR, { recursive: true })
writeFileSync(
join(TEST_CACHE_DIR, "package.json"),
JSON.stringify({ dependencies: { "oh-my-opencode": "3.10.0" } }, null, 2)
JSON.stringify({ dependencies: { "oh-my-openagent": "3.10.0" } }, null, 2)
)
const fs = await import("node:fs")
@@ -317,7 +317,7 @@ describe("syncCachePackageJsonToIntent", () => {
const { syncCachePackageJsonToIntent } = await import("./sync-package-json")
const pluginInfo: PluginEntryInfo = {
entry: "oh-my-opencode@latest",
entry: "oh-my-openagent@latest",
isPinned: false,
pinnedVersion: "latest",
configPath: "/tmp/opencode.json",
+2 -1
View File
@@ -2,8 +2,9 @@ import * as path from "node:path"
import * as os from "node:os"
import { getOpenCodeCacheDir } from "../../shared/data-path"
import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir"
import { PLUGIN_NAME } from "../../shared/plugin-identity"
export const PACKAGE_NAME = "oh-my-opencode"
export const PACKAGE_NAME = PLUGIN_NAME
export const NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`
export const NPM_FETCH_TIMEOUT = 5000
@@ -12,7 +12,7 @@ type ToastMessageGetter = (isUpdate: boolean, version?: string) => string
function createPluginEntry(overrides?: Partial<PluginEntry>): PluginEntry {
return {
entry: "oh-my-opencode@3.4.0",
entry: "oh-my-openagent@3.4.0",
isPinned: false,
pinnedVersion: null,
configPath: "/test/opencode.json",
@@ -95,7 +95,7 @@ describe("runBackgroundUpdateCheck", () => {
describe("#given no version available", () => {
it("returns early when neither cached nor pinned version exists", async () => {
//#given
mockFindPluginEntry.mockReturnValue(createPluginEntry({ entry: "oh-my-opencode" }))
mockFindPluginEntry.mockReturnValue(createPluginEntry({ entry: "oh-my-openagent" }))
mockGetCachedVersion.mockReturnValue(null)
//#when
await runBackgroundUpdateCheck(mockCtx, true, getToastMessage)
@@ -12,7 +12,7 @@ export async function showSpinnerToast(ctx: PluginInput, version: string, messag
await ctx.client.tui
.showToast({
body: {
title: `${spinner} OhMyOpenCode ${version}`,
title: `${spinner} OhMyOpenAgent ${version}`,
message,
variant: "info" as const,
duration: frameInterval + 50,
@@ -9,7 +9,7 @@ export async function showUpdateAvailableToast(
await ctx.client.tui
.showToast({
body: {
title: `OhMyOpenCode ${latestVersion}`,
title: `OhMyOpenAgent ${latestVersion}`,
message: getToastMessage(true, latestVersion),
variant: "info" as const,
duration: 8000,
@@ -23,7 +23,7 @@ export async function showAutoUpdatedToast(ctx: PluginInput, oldVersion: string,
await ctx.client.tui
.showToast({
body: {
title: "OhMyOpenCode Updated!",
title: "OhMyOpenAgent Updated!",
message: `v${oldVersion} → v${newVersion}\nRestart OpenCode to apply.`,
variant: "success" as const,
duration: 8000,
+7 -7
View File
@@ -40,7 +40,7 @@ const PLATFORM_MAP: Record<string, PlatformInfo> = {
}
/**
* Get the cache directory for oh-my-opencode binaries.
* Get the cache directory for oh-my-openagent binaries.
* On Windows: Uses %LOCALAPPDATA% or %APPDATA% (Windows conventions)
* On Unix: Follows XDG Base Directory Specification
*/
@@ -48,12 +48,12 @@ export function getCacheDir(): string {
if (process.platform === "win32") {
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA
const base = localAppData || join(homedir(), "AppData", "Local")
return join(base, "oh-my-opencode", "bin")
return join(base, "oh-my-openagent", "bin")
}
const xdgCache = process.env.XDG_CACHE_HOME
const base = xdgCache || join(homedir(), ".cache")
return join(base, "oh-my-opencode", "bin")
return join(base, "oh-my-openagent", "bin")
}
/**
@@ -113,7 +113,7 @@ export async function downloadCommentChecker(): Promise<string | null> {
const downloadUrl = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`
debugLog(`Downloading from: ${downloadUrl}`)
log(`[oh-my-opencode] Downloading comment-checker binary...`)
log(`[oh-my-openagent] Downloading comment-checker binary...`)
try {
// Ensure cache directory exists
@@ -139,14 +139,14 @@ export async function downloadCommentChecker(): Promise<string | null> {
ensureExecutable(binaryPath)
debugLog(`Successfully downloaded binary to: ${binaryPath}`)
log(`[oh-my-opencode] comment-checker binary ready.`)
log(`[oh-my-openagent] comment-checker binary ready.`)
return binaryPath
} catch (err) {
debugLog(`Failed to download: ${err}`)
log(`[oh-my-opencode] Failed to download comment-checker: ${err instanceof Error ? err.message : err}`)
log(`[oh-my-opencode] Comment checking disabled.`)
log(`[oh-my-openagent] Failed to download comment-checker: ${err instanceof Error ? err.message : err}`)
log(`[oh-my-openagent] Comment checking disabled.`)
return null
}
}
+1 -1
View File
@@ -137,7 +137,7 @@ describe("updateConnectedProvidersCache", () => {
test("does not remove unrelated files in the cache directory", async () => {
//#given
const realCacheDir = join(fakeUserCacheRoot, "oh-my-opencode")
const realCacheDir = join(fakeUserCacheRoot, "oh-my-openagent")
const sentinelPath = join(realCacheDir, "connected-providers-cache.test-sentinel.json")
mkdirSync(realCacheDir, { recursive: true })
writeFileSync(sentinelPath, JSON.stringify({ keep: true }))
+3 -3
View File
@@ -31,11 +31,11 @@ export function getCacheDir(): string {
}
/**
* Returns the oh-my-opencode cache directory.
* All platforms: ~/.cache/oh-my-opencode
* Returns the oh-my-openagent cache directory.
* All platforms: ~/.cache/oh-my-openagent
*/
export function getOmoOpenCodeCacheDir(): string {
return path.join(getCacheDir(), "oh-my-opencode")
return path.join(getCacheDir(), "oh-my-openagent")
}
/**
@@ -43,6 +43,24 @@ describe("external-plugin-detector", () => {
expect(result.allPlugins).toContain("oh-my-opencode")
})
test("should return detected=false when only oh-my-openagent is configured", () => {
// given - opencode.json with only oh-my-openagent
const opencodeDir = path.join(tempDir, ".opencode")
fs.mkdirSync(opencodeDir, { recursive: true })
fs.writeFileSync(
path.join(opencodeDir, "opencode.json"),
JSON.stringify({ plugin: ["oh-my-openagent"] })
)
// when
const result = detectExternalNotificationPlugin(tempDir)
// then
expect(result.detected).toBe(false)
expect(result.pluginName).toBeNull()
expect(result.allPlugins).toContain("oh-my-openagent")
})
test("should detect opencode-notifier plugin", () => {
// given - opencode.json with opencode-notifier
const opencodeDir = path.join(tempDir, ".opencode")
+7 -7
View File
@@ -1,5 +1,5 @@
/**
* Detects external plugins that may conflict with oh-my-opencode features.
* Detects external plugins that may conflict with oh-my-openagent features.
* Used to prevent crashes from concurrent notification plugins.
*/
@@ -14,7 +14,7 @@ interface OpencodeConfig {
}
/**
* Known notification plugins that conflict with oh-my-opencode's session-notification.
* Known notification plugins that conflict with oh-my-openagent's session-notification.
* Both plugins listen to session.idle and send notifications simultaneously,
* which can cause crashes on Windows due to resource contention.
*/
@@ -124,14 +124,14 @@ export function detectExternalNotificationPlugin(directory: string): ExternalNot
* Generate a warning message for users with conflicting notification plugins.
*/
export function getNotificationConflictWarning(pluginName: string): string {
return `[oh-my-opencode] External notification plugin detected: ${pluginName}
return `[oh-my-openagent] External notification plugin detected: ${pluginName}
Both oh-my-opencode and ${pluginName} listen to session.idle events.
Both oh-my-openagent and ${pluginName} listen to session.idle events.
Running both simultaneously can cause crashes on Windows.
oh-my-opencode's session-notification has been auto-disabled.
oh-my-openagent's session-notification has been auto-disabled.
To use oh-my-opencode's notifications instead, either:
To use oh-my-openagent's notifications instead, either:
1. Remove ${pluginName} from your opencode.json plugins
2. Or set "notification": { "force_enable": true } in oh-my-opencode.json`
2. Or set "notification": { "force_enable": true } in oh-my-openagent.json`
}
+1 -1
View File
@@ -4,7 +4,7 @@ import * as fs from "fs"
import * as os from "os"
import * as path from "path"
const logFile = path.join(os.tmpdir(), "oh-my-opencode.log")
const logFile = path.join(os.tmpdir(), "oh-my-openagent.log")
export function log(message: string, data?: unknown): void {
try {
+2 -2
View File
@@ -288,7 +288,7 @@ describe("opencode-config-dir", () => {
expect(paths.configJson).toBe(join(expectedDir, "opencode.json"))
expect(paths.configJsonc).toBe(join(expectedDir, "opencode.jsonc"))
expect(paths.packageJson).toBe(join(expectedDir, "package.json"))
expect(paths.omoConfig).toBe(join(expectedDir, "oh-my-opencode.json"))
expect(paths.omoConfig).toBe(join(expectedDir, "oh-my-openagent.json"))
})
test("returns all config paths for desktop binary", () => {
@@ -304,7 +304,7 @@ describe("opencode-config-dir", () => {
expect(paths.configJson).toBe(join(expectedDir, "opencode.json"))
expect(paths.configJsonc).toBe(join(expectedDir, "opencode.jsonc"))
expect(paths.packageJson).toBe(join(expectedDir, "package.json"))
expect(paths.omoConfig).toBe(join(expectedDir, "oh-my-opencode.json"))
expect(paths.omoConfig).toBe(join(expectedDir, "oh-my-openagent.json"))
})
})
+1 -1
View File
@@ -83,7 +83,7 @@ export function getOpenCodeConfigPaths(options: OpenCodeConfigDirOptions): OpenC
configJson: join(configDir, "opencode.json"),
configJsonc: join(configDir, "opencode.jsonc"),
packageJson: join(configDir, "package.json"),
omoConfig: join(configDir, "oh-my-opencode.json"),
omoConfig: join(configDir, "oh-my-openagent.json")
}
}
+5 -5
View File
@@ -1,23 +1,23 @@
/**
* Unified system directive prefix for oh-my-opencode internal messages.
* Unified system directive prefix for oh-my-openagent internal messages.
* All system-generated messages should use this prefix for consistent filtering.
*
* Format: [SYSTEM DIRECTIVE: OH-MY-OPENCODE - {TYPE}]
* Format: [SYSTEM DIRECTIVE: OH-MY-OPENAGENT - {TYPE}]
*/
export const SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: OH-MY-OPENCODE"
export const SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: OH-MY-OPENAGENT"
/**
* Creates a system directive header with the given type.
* @param type - The directive type (e.g., "TODO CONTINUATION", "RALPH LOOP")
* @returns Formatted directive string like "[SYSTEM DIRECTIVE: OH-MY-OPENCODE - TODO CONTINUATION]"
* @returns Formatted directive string like "[SYSTEM DIRECTIVE: OH-MY-OPENAGENT - TODO CONTINUATION]"
*/
export function createSystemDirective(type: string): string {
return `${SYSTEM_DIRECTIVE_PREFIX} - ${type}]`
}
/**
* Checks if a message starts with the oh-my-opencode system directive prefix.
* Checks if a message starts with the oh-my-openagent system directive prefix.
* Used by keyword-detector and other hooks to skip system-generated messages.
* @param text - The message text to check
* @returns true if the message is a system directive
+6 -8
View File
@@ -47,12 +47,12 @@ export function getCacheDir(): string {
if (process.platform === "win32") {
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA
const base = localAppData || join(homedir(), "AppData", "Local")
return join(base, "oh-my-opencode", "bin")
return join(base, "oh-my-openagent", "bin")
}
const xdgCache = process.env.XDG_CACHE_HOME
const base = xdgCache || join(homedir(), ".cache")
return join(base, "oh-my-opencode", "bin")
return join(base, "oh-my-openagent", "bin")
}
export function getBinaryName(): string {
@@ -63,14 +63,12 @@ export function getCachedBinaryPath(): string | null {
return getCachedBinaryPathShared(getCacheDir(), getBinaryName())
}
export async function downloadAstGrep(version: string = DEFAULT_VERSION): Promise<string | null> {
const platformKey = `${process.platform}-${process.arch}`
const platformInfo = PLATFORM_MAP[platformKey]
if (!platformInfo) {
log(`[oh-my-opencode] Unsupported platform for ast-grep: ${platformKey}`)
log(`[oh-my-openagent] Unsupported platform for ast-grep: ${platformKey}`)
return null
}
@@ -86,7 +84,7 @@ export async function downloadAstGrep(version: string = DEFAULT_VERSION): Promis
const assetName = `app-${arch}-${os}.zip`
const downloadUrl = `https://github.com/${REPO}/releases/download/${version}/${assetName}`
log(`[oh-my-opencode] Downloading ast-grep binary...`)
log(`[oh-my-openagent] Downloading ast-grep binary...`)
try {
const archivePath = join(cacheDir, assetName)
@@ -96,12 +94,12 @@ export async function downloadAstGrep(version: string = DEFAULT_VERSION): Promis
cleanupArchive(archivePath)
ensureExecutable(binaryPath)
log(`[oh-my-opencode] ast-grep binary ready.`)
log(`[oh-my-openagent] ast-grep binary ready.`)
return binaryPath
} catch (err) {
log(
`[oh-my-opencode] Failed to download ast-grep: ${err instanceof Error ? err.message : err}`
`[oh-my-openagent] Failed to download ast-grep: ${err instanceof Error ? err.message : err}`
)
return null
}
+1 -1
View File
@@ -79,7 +79,7 @@ export function createCallOmoAgent(
// Check if agent is disabled
if (disabledAgents.some((disabled) => disabled.toLowerCase() === normalizedAgent)) {
return `Error: Agent "${normalizedAgent}" is disabled via disabled_agents configuration. Remove it from disabled_agents in your oh-my-opencode.json to use it.`
return `Error: Agent "${normalizedAgent}" is disabled via disabled_agents configuration. Remove it from disabled_agents in your oh-my-openagent.json to use it.`
}
const fallbackChain = resolveFallbackChainForCallOmoAgent({
+2 -2
View File
@@ -62,7 +62,7 @@ export async function resolveCategoryExecution(
To use this category:
1. Connect a provider with this model: ${requirement.requiresModel}
2. Or configure an alternative model in your oh-my-opencode.json for this category
2. Or configure an alternative model in your oh-my-openagent.json for this category
Available categories: ${allCategoryNames}`,
}
@@ -178,7 +178,7 @@ Available categories: ${allCategoryNames}`,
Configure in one of:
1. OpenCode: Set "model" in opencode.json
2. Oh-My-OpenCode: Set category model in oh-my-opencode.json
2. Oh-My-OpenAgent: Set category model in oh-my-openagent.json
3. Provider: Connect a provider with available models
Current category: ${args.category}
+1 -1
View File
@@ -39,7 +39,7 @@ function getPlatformKey(): string {
function getInstallDir(): string {
const homeDir = process.env.HOME || process.env.USERPROFILE || "."
return join(homeDir, ".cache", "oh-my-opencode", "bin")
return join(homeDir, ".cache", "oh-my-openagent", "bin")
}
function getRgPath(): string {
+1 -1
View File
@@ -63,7 +63,7 @@ export function formatServerLookupError(result: Exclude<ServerLookupResult, { st
``,
`Available servers: ${result.availableServers.slice(0, 10).join(", ")}${result.availableServers.length > 10 ? "..." : ""}`,
``,
`To add a custom server, configure 'lsp' in oh-my-opencode.json:`,
`To add a custom server, configure 'lsp' in oh-my-openagent.json:`,
` {`,
` "lsp": {`,
` "my-server": {`,
+4 -4
View File
@@ -53,7 +53,7 @@ describe("loadJsonFile", () => {
}
}
}`
const userPath = join(tempBase, "oh-my-opencode.jsonc")
const userPath = join(tempBase, "oh-my-openagent.jsonc")
writeFileSync(userPath, userJsonc, "utf-8")
const servers = getMergedServers()
@@ -109,7 +109,7 @@ describe("loadJsonFile", () => {
}
}
}`
const projectPath = join(tempProject, ".opencode", "oh-my-opencode.jsonc")
const projectPath = join(tempProject, ".opencode", "oh-my-openagent.jsonc")
writeFileSync(projectPath, projectJsonc, "utf-8")
process.chdir(tempProject)
@@ -146,8 +146,8 @@ describe("loadJsonFile", () => {
}
}
}`
writeFileSync(join(tempBase, "oh-my-opencode.json"), jsonContent, "utf-8")
writeFileSync(join(tempBase, "oh-my-opencode.jsonc"), jsoncContent, "utf-8")
writeFileSync(join(tempBase, "oh-my-openagent.json"), jsonContent, "utf-8")
writeFileSync(join(tempBase, "oh-my-openagent.jsonc"), jsoncContent, "utf-8")
const servers = getMergedServers()
const found = servers.find(s => s.id === "conflict" && s.source === "user")
+3 -2
View File
@@ -5,6 +5,7 @@ import { BUILTIN_SERVERS } from "./constants"
import type { ResolvedServer } from "./types"
import { getOpenCodeConfigDir } from "../../shared"
import { parseJsonc, detectConfigFile } from "../../shared/jsonc-parser"
import { CONFIG_BASENAME } from "../../shared/plugin-identity"
interface LspEntry {
disabled?: boolean
@@ -38,8 +39,8 @@ export function getConfigPaths(): { project: string; user: string; opencode: str
const cwd = process.cwd()
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
return {
project: detectConfigFile(join(cwd, ".opencode", "oh-my-opencode")).path,
user: detectConfigFile(join(configDir, "oh-my-opencode")).path,
project: detectConfigFile(join(cwd, ".opencode", CONFIG_BASENAME)).path,
user: detectConfigFile(join(configDir, CONFIG_BASENAME)).path,
opencode: detectConfigFile(join(configDir, "opencode")).path,
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ export function isServerInstalled(command: string[]): boolean {
}
}
// Runtime wrappers (bun/node) are always available in oh-my-opencode context
// Runtime wrappers (bun/node) are always available in oh-my-openagent context
if (cmd === "bun" || cmd === "node") {
return true
}