Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3e4336d
fix: update omoPluginName from oh-my-opencode to oh-my-openagent (#2600)
lark1115 Apr 4, 2026
baa9f22
fix: update all oh-my-opencode references to oh-my-openagent
lark1115 Apr 4, 2026
0465949
fix: add legacy oh-my-opencode config fallback for existing users
lark1115 Apr 4, 2026
197868d
fix: correct try?/?? operator precedence in legacy config fallback
lark1115 Apr 4, 2026
5fde3d3
refactor: M10 cleanup — finish short ID ref migration + remove SSH po…
Jesssullivan Apr 5, 2026
537e9c1
Add configurable close confirmation settings
arieltobiana Apr 5, 2026
22bfae5
Fix batch close bypassing per-workspace running-process confirmation
arieltobiana Apr 5, 2026
6b26bc0
Fix batch close bypassing pinned workspace confirmation
arieltobiana Apr 5, 2026
4764feb
Use workspace tab color for selected sidebar rows
austinywang Apr 3, 2026
d7d0869
Fix locale-formatted port numbers and PR IDs in sidebar
shuhei0866 Apr 4, 2026
bf7f44c
Update xcstrings format specifiers and localize PR label text
shuhei0866 Apr 4, 2026
d6bfbee
Revert unintended xcstrings change in accessibility.workspacePosition
shuhei0866 Apr 4, 2026
7cc42a1
Add table of contents sidebar and search to settings window
lawrencecchen Apr 4, 2026
e8df0e8
wip
austinywang Apr 3, 2026
96ef41e
fix: avoid tmux overlay re-entrancy hang
austinywang Apr 4, 2026
9ac6740
fix: preserve git metadata refresh retries
austinywang Apr 4, 2026
40448f5
Fix sidebar PR status polling rate limits
austinywang Apr 4, 2026
75368ed
Suppress nested split portal sync during divider drag
austinywang Apr 4, 2026
e869f00
Fix clipboard callback build break
austinywang Apr 4, 2026
6fedcbe
Fix reload build blockers
austinywang Apr 4, 2026
279d14e
Correct clipboard callback returns
austinywang Apr 4, 2026
0098472
Fix ghostty callback return values
austinywang Apr 4, 2026
190d8b5
Fix Ghostty clipboard callback return type
austinywang Apr 4, 2026
4a25da3
fix(notifications): push mark-unread notifications to top of list
Jesssullivan Apr 5, 2026
8277550
fix(ui): ring flash CAKeyframeAnimation + notification polish
Jesssullivan Apr 5, 2026
cddbd6d
docs: mark notification polish + tab rename as done in TODO (27/34)
Jesssullivan Apr 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 31 additions & 21 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9853,7 +9853,7 @@ struct CMUXCLI {
}
}

private static let omoPluginName = "oh-my-opencode"
private static let omoPluginName = "oh-my-openagent"

private func resolveExecutableInPath(_ name: String) -> String? {
let entries = ProcessInfo.processInfo.environment["PATH"]?.split(separator: ":").map(String.init) ?? []
Expand Down Expand Up @@ -9895,7 +9895,7 @@ struct CMUXCLI {

// Keep the shadow package isolated from stale/yanked pins in the user's
// opencode package.json. bun will update this manifest with the resolved
// oh-my-opencode version when installation succeeds.
// oh-my-openagent version when installation succeeds.
let packageManifest: [String: Any] = [
"dependencies": [
Self.omoPluginName: "latest"
Expand Down Expand Up @@ -10039,7 +10039,7 @@ struct CMUXCLI {
return "4096"
}

/// Creates a shadow config directory that layers oh-my-opencode on top of the user's
/// Creates a shadow config directory that layers oh-my-openagent on top of the user's
/// existing opencode config without modifying the original. Sets OPENCODE_CONFIG_DIR
/// to point at the shadow directory.
private func omoEnsurePlugin() throws {
Expand All @@ -10064,6 +10064,8 @@ struct CMUXCLI {
}

var plugins = (config["plugin"] as? [String]) ?? []
// Remove legacy oh-my-opencode entries to avoid duplicate plugin registration
plugins.removeAll { $0 == "oh-my-opencode" || $0.hasPrefix("oh-my-opencode@") }
let alreadyPresent = plugins.contains {
$0 == Self.omoPluginName || $0.hasPrefix("\(Self.omoPluginName)@")
}
Expand All @@ -10089,12 +10091,18 @@ struct CMUXCLI {
try? fm.removeItem(at: shadowBunLockURL)
}

// Copy oh-my-opencode plugin config (jsonc) if the user has one
for filename in ["oh-my-opencode.json", "oh-my-opencode.jsonc"] {
let userFile = userDir.appendingPathComponent(filename)
let shadowFile = shadowDir.appendingPathComponent(filename)
if fm.fileExists(atPath: userFile.path) && !fm.fileExists(atPath: shadowFile.path) {
// Copy oh-my-openagent plugin config if the user has one (fall back to legacy oh-my-opencode name)
for ext in ["json", "jsonc"] {
let newName = "oh-my-openagent.\(ext)"
let legacyName = "oh-my-opencode.\(ext)"
let shadowFile = shadowDir.appendingPathComponent(newName)
guard !fm.fileExists(atPath: shadowFile.path) else { continue }
let userFile = userDir.appendingPathComponent(newName)
let legacyFile = userDir.appendingPathComponent(legacyName)
if fm.fileExists(atPath: userFile.path) {
try fm.createSymbolicLink(at: shadowFile, withDestinationURL: userFile)
} else if fm.fileExists(atPath: legacyFile.path) {
try fm.createSymbolicLink(at: shadowFile, withDestinationURL: legacyFile)
}
}

Expand All @@ -10103,15 +10111,15 @@ struct CMUXCLI {
if !fm.fileExists(atPath: pluginPackageDir.path) {
let installDir = shadowDir
if let bunPath = resolveExecutableInPath("bun") {
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
FileHandle.standardError.write("Installing oh-my-openagent plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
let installArguments = ["add", Self.omoPluginName]
let firstAttemptStatus = try omoRunPackageInstall(
executablePath: bunPath,
arguments: installArguments,
currentDirectoryURL: installDir
)
if firstAttemptStatus != 0 {
FileHandle.standardError.write("Retrying oh-my-opencode install with a clean shadow package state...\n".data(using: .utf8)!)
FileHandle.standardError.write("Retrying oh-my-openagent install with a clean shadow package state...\n".data(using: .utf8)!)
try? fm.removeItem(at: shadowBunLockURL)
try? fm.removeItem(at: shadowNodeModules)
try omoEnsureShadowNodeModulesSymlink(shadowNodeModules: shadowNodeModules, userNodeModules: userNodeModules)
Expand All @@ -10121,37 +10129,39 @@ struct CMUXCLI {
currentDirectoryURL: installDir
)
if retryStatus != 0 {
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
throw CLIError(message: "Failed to install oh-my-openagent. Try manually: npm install -g oh-my-openagent")
}
}
} else if let npmPath = resolveExecutableInPath("npm") {
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
FileHandle.standardError.write("Installing oh-my-openagent plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
let status = try omoRunPackageInstall(
executablePath: npmPath,
arguments: ["install", Self.omoPluginName],
currentDirectoryURL: installDir
)
if status != 0 {
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
throw CLIError(message: "Failed to install oh-my-openagent. Try manually: npm install -g oh-my-openagent")
}
} else {
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-opencode manually: bunx oh-my-opencode install")
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-openagent manually: bunx oh-my-openagent install")
}
FileHandle.standardError.write("oh-my-opencode plugin installed\n".data(using: .utf8)!)
FileHandle.standardError.write("oh-my-openagent plugin installed\n".data(using: .utf8)!)
}

// Ensure tmux mode is enabled in oh-my-opencode config.
// Ensure tmux mode is enabled in oh-my-openagent config.
// Without this, the TmuxSessionManager won't spawn visual panes even though
// $TMUX is set (tmux.enabled defaults to false).
let omoConfigURL = shadowDir.appendingPathComponent("oh-my-opencode.json")
let omoConfigURL = shadowDir.appendingPathComponent("oh-my-openagent.json")
var omoConfig: [String: Any]
if let data = try? Data(contentsOf: omoConfigURL),
let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
omoConfig = existing
} else {
// Check if user has a config we symlinked, read from source
let userOmoConfig = userDir.appendingPathComponent("oh-my-opencode.json")
if let data = try? Data(contentsOf: userOmoConfig),
// Check if user has a config we symlinked, read from source (new name first, fall back to legacy)
let userOmoConfig = userDir.appendingPathComponent("oh-my-openagent.json")
let legacyOmoConfig = userDir.appendingPathComponent("oh-my-opencode.json")
let userConfigData = (try? Data(contentsOf: userOmoConfig)) ?? (try? Data(contentsOf: legacyOmoConfig))
if let data = userConfigData,
let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
omoConfig = existing
// Remove the symlink so we can write our own copy
Expand Down Expand Up @@ -10248,7 +10258,7 @@ struct CMUXCLI {
}
}

// Ensure oh-my-opencode plugin is registered and installed
// Ensure oh-my-openagent plugin is registered and installed
try omoEnsurePlugin()

let shimDirectory = try createOMOShimDirectory()
Expand Down
84 changes: 59 additions & 25 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -63173,6 +63173,23 @@
}
}
},
"settings.search.placeholder": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Search settings"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "設定を検索"
}
}
}
},
"settings.section.workspaceColors": {
"extractionState": "manual",
"localizations": {
Expand Down Expand Up @@ -70066,19 +70083,19 @@
"en": {
"stringUnit": {
"state": "translated",
"value": ":%lld"
"value": ":%@"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": ":%lld"
"value": ":%@"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": ":%lld"
"value": ":%@"
}
}
}
Expand All @@ -70089,19 +70106,36 @@
"en": {
"stringUnit": {
"state": "translated",
"value": "Open localhost:%lld"
"value": "Open localhost:%@"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "localhost:%lldを開く"
"value": "localhost:%@を開く"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Відкрити localhost:%lld"
"value": "Відкрити localhost:%@"
}
}
}
},
"sidebar.pullRequest.label": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$@"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$@"
}
}
}
Expand All @@ -70112,115 +70146,115 @@
"en": {
"stringUnit": {
"state": "translated",
"value": "Open %1$@ #%2$lld"
"value": "Open %1$@ #%2$@"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$lldを開く"
"value": "%1$@ #%2$@を開く"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "打开 %1$@ #%2$lld"
"value": "打开 %1$@ #%2$@"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "開啟 %1$@ #%2$lld"
"value": "開啟 %1$@ #%2$@"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$lld 열기"
"value": "%1$@ #%2$@ 열기"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$lld öffnen"
"value": "%1$@ #%2$@ öffnen"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Abrir %1$@ #%2$lld"
"value": "Abrir %1$@ #%2$@"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Ouvrir %1$@ #%2$lld"
"value": "Ouvrir %1$@ #%2$@"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Apri %1$@ #%2$lld"
"value": "Apri %1$@ #%2$@"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Åbn %1$@ #%2$lld"
"value": "Åbn %1$@ #%2$@"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Otwórz %1$@ #%2$lld"
"value": "Otwórz %1$@ #%2$@"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Открыть %1$@ #%2$lld"
"value": "Открыть %1$@ #%2$@"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Otvori %1$@ #%2$lld"
"value": "Otvori %1$@ #%2$@"
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "فتح %1$@ #%2$lld"
"value": "فتح %1$@ #%2$@"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Åpne %1$@ #%2$lld"
"value": "Åpne %1$@ #%2$@"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Abrir %1$@ #%2$lld"
"value": "Abrir %1$@ #%2$@"
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "เปิด %1$@ #%2$lld"
"value": "เปิด %1$@ #%2$@"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "%1$@ #%2$lld Aç"
"value": "%1$@ #%2$@ Aç"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Відкрити %1$@ #%2$lld"
"value": "Відкрити %1$@ #%2$@"
}
}
}
Expand Down
Loading
Loading