Skip to content

Commit e8530f6

Browse files
Jesssullivanlark1115arieltobianaaustinywangshuhei0866
authored
M10: cleanup sprint + community parity (#178)
* fix: update omoPluginName from oh-my-opencode to oh-my-openagent (manaflow-ai#2600) The npm package was renamed from oh-my-opencode to oh-my-openagent. The hardcoded old name in the shadow package.json causes a legacy-name warning on every startup and breaks symlinked opencode.json files via automatic migration. * fix: update all oh-my-opencode references to oh-my-openagent - Update config filenames (oh-my-opencode.json → oh-my-openagent.json) - Update all user-facing error messages and status strings - Update all comments - Add legacy plugin name cleanup to prevent duplicate registration when upgrading from oh-my-opencode to oh-my-openagent * fix: add legacy oh-my-opencode config fallback for existing users Existing users may only have oh-my-opencode.json in their config dir. Add fallback logic to symlink and read legacy filenames when the new oh-my-openagent.json is not present, in both Swift and Go paths. * fix: correct try?/?? operator precedence in legacy config fallback try? Data(contentsOf: a) ?? Data(contentsOf: b) is parsed as try? (Data(...) ?? Data(...)), so the fallback never fires. Use explicit parentheses: (try? Data(...)) ?? (try? Data(...)) * refactor: M10 cleanup — finish short ID ref migration + remove SSH port mirroring - Remove deprecated index params from workspace.reorder (socket.zig) - Remove index params from Python client (reorder_workspace, move_surface, reorder_surface) - Update test_workspace_reorder to use before_workspace instead of index - Remove remoteForwardedPorts property and forwarded_ports payload field - Update SSH remote tests to assert forwarded_ports field is absent - Mark 19 of 27 TODO items as complete (reflecting actual state) * Add configurable close confirmation settings Each close confirmation dialog can now be independently toggled in Settings > App: - Confirm Close Running Process (workspace with running command) - Confirm Close Pinned Workspace - Confirm Close Window - Confirm Batch Close (multiple tabs/workspaces) All default to enabled, preserving existing behavior. Also configurable via `defaults write com.cmuxterm.app <key> -bool false`. * Fix batch close bypassing per-workspace running-process confirmation When batch confirmation is disabled, each workspace should still get its own running-process confirmation check. Previously, requiresConfirmation was always false after the batch dialog, which silently skipped per-workspace checks when the batch dialog was disabled. * Fix batch close bypassing pinned workspace confirmation When batch confirmation is disabled, route each workspace through closeWorkspaceWithConfirmation so pinned workspace and running-process confirmations are each independently honored. * Use workspace tab color for selected sidebar rows * Fix locale-formatted port numbers and PR IDs in sidebar * Update xcstrings format specifiers and localize PR label text - Change %lld to %@ in Localizable.xcstrings for port and PR keys to match the new String argument type - Wrap PR label Text in String(localized:) for localization consistency - Add sidebar.pullRequest.label key to xcstrings * Revert unintended xcstrings change in accessibility.workspacePosition The previous replace_all on %2$lld inadvertently changed the accessibility.workspacePosition key across 18 locales. Reverted since the Swift source still passes Int for that key. * Add table of contents sidebar and search to settings window Settings window now has a 160px sidebar with: - Search field to filter sections by keyword - Section list with icons for quick navigation - Active section tracking based on scroll position The sidebar search matches against section titles and individual setting labels within each section. Non-matching sections are hidden from both the sidebar list and the main content area. Window width increased from 640 to 800 to accommodate the sidebar. * wip * fix: avoid tmux overlay re-entrancy hang * fix: preserve git metadata refresh retries * Fix sidebar PR status polling rate limits * Suppress nested split portal sync during divider drag * Fix clipboard callback build break * Fix reload build blockers * Correct clipboard callback returns * Fix ghostty callback return values * Fix Ghostty clipboard callback return type * fix(notifications): push mark-unread notifications to top of list markUnread(forTabId:) now moves newly-unread notifications to index 0, matching addNotification() behavior. Previously it only flipped isRead without reordering. * fix(ui): ring flash CAKeyframeAnimation + notification polish Ring flash fix: - Add shared PanelFlashOverlayNSView with CAKeyframeAnimation in Panel.swift - Replace SwiftUI withAnimation flash in BrowserPanelView (was coalescing to 1 flash) - Replace SwiftUI withAnimation flash in MarkdownPanelView (same bug) - Both panels now flash twice consistently, matching terminal behavior Notification polish: - Add hover/focus outline on notification rows (accent border) - Add right-click context menu with Mark Read/Unread + Clear actions TODO.md: - Mark ring flash, question mark icon, tab rename as done (23/27 complete) * docs: mark notification polish + tab rename as done in TODO (27/34) --------- Co-authored-by: lark <lark1115caster@gmail.com> Co-authored-by: Ariel Tobiana <arieltobiana@gmail.com> Co-authored-by: austinpower1258 <austinwang115@gmail.com> Co-authored-by: Shuhei0866 <shuhei0866@gmail.com> Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
1 parent 1c52012 commit e8530f6

27 files changed

Lines changed: 2399 additions & 651 deletions

CLI/cmux.swift

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9853,7 +9853,7 @@ struct CMUXCLI {
98539853
}
98549854
}
98559855

9856-
private static let omoPluginName = "oh-my-opencode"
9856+
private static let omoPluginName = "oh-my-openagent"
98579857

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

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

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

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

10092-
// Copy oh-my-opencode plugin config (jsonc) if the user has one
10093-
for filename in ["oh-my-opencode.json", "oh-my-opencode.jsonc"] {
10094-
let userFile = userDir.appendingPathComponent(filename)
10095-
let shadowFile = shadowDir.appendingPathComponent(filename)
10096-
if fm.fileExists(atPath: userFile.path) && !fm.fileExists(atPath: shadowFile.path) {
10094+
// Copy oh-my-openagent plugin config if the user has one (fall back to legacy oh-my-opencode name)
10095+
for ext in ["json", "jsonc"] {
10096+
let newName = "oh-my-openagent.\(ext)"
10097+
let legacyName = "oh-my-opencode.\(ext)"
10098+
let shadowFile = shadowDir.appendingPathComponent(newName)
10099+
guard !fm.fileExists(atPath: shadowFile.path) else { continue }
10100+
let userFile = userDir.appendingPathComponent(newName)
10101+
let legacyFile = userDir.appendingPathComponent(legacyName)
10102+
if fm.fileExists(atPath: userFile.path) {
1009710103
try fm.createSymbolicLink(at: shadowFile, withDestinationURL: userFile)
10104+
} else if fm.fileExists(atPath: legacyFile.path) {
10105+
try fm.createSymbolicLink(at: shadowFile, withDestinationURL: legacyFile)
1009810106
}
1009910107
}
1010010108

@@ -10103,15 +10111,15 @@ struct CMUXCLI {
1010310111
if !fm.fileExists(atPath: pluginPackageDir.path) {
1010410112
let installDir = shadowDir
1010510113
if let bunPath = resolveExecutableInPath("bun") {
10106-
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
10114+
FileHandle.standardError.write("Installing oh-my-openagent plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
1010710115
let installArguments = ["add", Self.omoPluginName]
1010810116
let firstAttemptStatus = try omoRunPackageInstall(
1010910117
executablePath: bunPath,
1011010118
arguments: installArguments,
1011110119
currentDirectoryURL: installDir
1011210120
)
1011310121
if firstAttemptStatus != 0 {
10114-
FileHandle.standardError.write("Retrying oh-my-opencode install with a clean shadow package state...\n".data(using: .utf8)!)
10122+
FileHandle.standardError.write("Retrying oh-my-openagent install with a clean shadow package state...\n".data(using: .utf8)!)
1011510123
try? fm.removeItem(at: shadowBunLockURL)
1011610124
try? fm.removeItem(at: shadowNodeModules)
1011710125
try omoEnsureShadowNodeModulesSymlink(shadowNodeModules: shadowNodeModules, userNodeModules: userNodeModules)
@@ -10121,37 +10129,39 @@ struct CMUXCLI {
1012110129
currentDirectoryURL: installDir
1012210130
)
1012310131
if retryStatus != 0 {
10124-
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
10132+
throw CLIError(message: "Failed to install oh-my-openagent. Try manually: npm install -g oh-my-openagent")
1012510133
}
1012610134
}
1012710135
} else if let npmPath = resolveExecutableInPath("npm") {
10128-
FileHandle.standardError.write("Installing oh-my-opencode plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
10136+
FileHandle.standardError.write("Installing oh-my-openagent plugin (this may take a minute on first run)...\n".data(using: .utf8)!)
1012910137
let status = try omoRunPackageInstall(
1013010138
executablePath: npmPath,
1013110139
arguments: ["install", Self.omoPluginName],
1013210140
currentDirectoryURL: installDir
1013310141
)
1013410142
if status != 0 {
10135-
throw CLIError(message: "Failed to install oh-my-opencode. Try manually: npm install -g oh-my-opencode")
10143+
throw CLIError(message: "Failed to install oh-my-openagent. Try manually: npm install -g oh-my-openagent")
1013610144
}
1013710145
} else {
10138-
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-opencode manually: bunx oh-my-opencode install")
10146+
throw CLIError(message: "Neither bun nor npm found in PATH. Install oh-my-openagent manually: bunx oh-my-openagent install")
1013910147
}
10140-
FileHandle.standardError.write("oh-my-opencode plugin installed\n".data(using: .utf8)!)
10148+
FileHandle.standardError.write("oh-my-openagent plugin installed\n".data(using: .utf8)!)
1014110149
}
1014210150

10143-
// Ensure tmux mode is enabled in oh-my-opencode config.
10151+
// Ensure tmux mode is enabled in oh-my-openagent config.
1014410152
// Without this, the TmuxSessionManager won't spawn visual panes even though
1014510153
// $TMUX is set (tmux.enabled defaults to false).
10146-
let omoConfigURL = shadowDir.appendingPathComponent("oh-my-opencode.json")
10154+
let omoConfigURL = shadowDir.appendingPathComponent("oh-my-openagent.json")
1014710155
var omoConfig: [String: Any]
1014810156
if let data = try? Data(contentsOf: omoConfigURL),
1014910157
let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
1015010158
omoConfig = existing
1015110159
} else {
10152-
// Check if user has a config we symlinked, read from source
10153-
let userOmoConfig = userDir.appendingPathComponent("oh-my-opencode.json")
10154-
if let data = try? Data(contentsOf: userOmoConfig),
10160+
// Check if user has a config we symlinked, read from source (new name first, fall back to legacy)
10161+
let userOmoConfig = userDir.appendingPathComponent("oh-my-openagent.json")
10162+
let legacyOmoConfig = userDir.appendingPathComponent("oh-my-opencode.json")
10163+
let userConfigData = (try? Data(contentsOf: userOmoConfig)) ?? (try? Data(contentsOf: legacyOmoConfig))
10164+
if let data = userConfigData,
1015510165
let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
1015610166
omoConfig = existing
1015710167
// Remove the symlink so we can write our own copy
@@ -10248,7 +10258,7 @@ struct CMUXCLI {
1024810258
}
1024910259
}
1025010260

10251-
// Ensure oh-my-opencode plugin is registered and installed
10261+
// Ensure oh-my-openagent plugin is registered and installed
1025210262
try omoEnsurePlugin()
1025310263

1025410264
let shimDirectory = try createOMOShimDirectory()

Resources/Localizable.xcstrings

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63173,6 +63173,23 @@
6317363173
}
6317463174
}
6317563175
},
63176+
"settings.search.placeholder": {
63177+
"extractionState": "manual",
63178+
"localizations": {
63179+
"en": {
63180+
"stringUnit": {
63181+
"state": "translated",
63182+
"value": "Search settings"
63183+
}
63184+
},
63185+
"ja": {
63186+
"stringUnit": {
63187+
"state": "translated",
63188+
"value": "設定を検索"
63189+
}
63190+
}
63191+
}
63192+
},
6317663193
"settings.section.workspaceColors": {
6317763194
"extractionState": "manual",
6317863195
"localizations": {
@@ -70066,19 +70083,19 @@
7006670083
"en": {
7006770084
"stringUnit": {
7006870085
"state": "translated",
70069-
"value": ":%lld"
70086+
"value": ":%@"
7007070087
}
7007170088
},
7007270089
"ja": {
7007370090
"stringUnit": {
7007470091
"state": "translated",
70075-
"value": ":%lld"
70092+
"value": ":%@"
7007670093
}
7007770094
},
7007870095
"uk": {
7007970096
"stringUnit": {
7008070097
"state": "translated",
70081-
"value": ":%lld"
70098+
"value": ":%@"
7008270099
}
7008370100
}
7008470101
}
@@ -70089,19 +70106,36 @@
7008970106
"en": {
7009070107
"stringUnit": {
7009170108
"state": "translated",
70092-
"value": "Open localhost:%lld"
70109+
"value": "Open localhost:%@"
7009370110
}
7009470111
},
7009570112
"ja": {
7009670113
"stringUnit": {
7009770114
"state": "translated",
70098-
"value": "localhost:%lldを開く"
70115+
"value": "localhost:%@を開く"
7009970116
}
7010070117
},
7010170118
"uk": {
7010270119
"stringUnit": {
7010370120
"state": "translated",
70104-
"value": "Відкрити localhost:%lld"
70121+
"value": "Відкрити localhost:%@"
70122+
}
70123+
}
70124+
}
70125+
},
70126+
"sidebar.pullRequest.label": {
70127+
"extractionState": "manual",
70128+
"localizations": {
70129+
"en": {
70130+
"stringUnit": {
70131+
"state": "translated",
70132+
"value": "%1$@ #%2$@"
70133+
}
70134+
},
70135+
"ja": {
70136+
"stringUnit": {
70137+
"state": "translated",
70138+
"value": "%1$@ #%2$@"
7010570139
}
7010670140
}
7010770141
}
@@ -70112,115 +70146,115 @@
7011270146
"en": {
7011370147
"stringUnit": {
7011470148
"state": "translated",
70115-
"value": "Open %1$@ #%2$lld"
70149+
"value": "Open %1$@ #%2$@"
7011670150
}
7011770151
},
7011870152
"ja": {
7011970153
"stringUnit": {
7012070154
"state": "translated",
70121-
"value": "%1$@ #%2$lldを開く"
70155+
"value": "%1$@ #%2$@を開く"
7012270156
}
7012370157
},
7012470158
"zh-Hans": {
7012570159
"stringUnit": {
7012670160
"state": "translated",
70127-
"value": "打开 %1$@ #%2$lld"
70161+
"value": "打开 %1$@ #%2$@"
7012870162
}
7012970163
},
7013070164
"zh-Hant": {
7013170165
"stringUnit": {
7013270166
"state": "translated",
70133-
"value": "開啟 %1$@ #%2$lld"
70167+
"value": "開啟 %1$@ #%2$@"
7013470168
}
7013570169
},
7013670170
"ko": {
7013770171
"stringUnit": {
7013870172
"state": "translated",
70139-
"value": "%1$@ #%2$lld 열기"
70173+
"value": "%1$@ #%2$@ 열기"
7014070174
}
7014170175
},
7014270176
"de": {
7014370177
"stringUnit": {
7014470178
"state": "translated",
70145-
"value": "%1$@ #%2$lld öffnen"
70179+
"value": "%1$@ #%2$@ öffnen"
7014670180
}
7014770181
},
7014870182
"es": {
7014970183
"stringUnit": {
7015070184
"state": "translated",
70151-
"value": "Abrir %1$@ #%2$lld"
70185+
"value": "Abrir %1$@ #%2$@"
7015270186
}
7015370187
},
7015470188
"fr": {
7015570189
"stringUnit": {
7015670190
"state": "translated",
70157-
"value": "Ouvrir %1$@ #%2$lld"
70191+
"value": "Ouvrir %1$@ #%2$@"
7015870192
}
7015970193
},
7016070194
"it": {
7016170195
"stringUnit": {
7016270196
"state": "translated",
70163-
"value": "Apri %1$@ #%2$lld"
70197+
"value": "Apri %1$@ #%2$@"
7016470198
}
7016570199
},
7016670200
"da": {
7016770201
"stringUnit": {
7016870202
"state": "translated",
70169-
"value": "Åbn %1$@ #%2$lld"
70203+
"value": "Åbn %1$@ #%2$@"
7017070204
}
7017170205
},
7017270206
"pl": {
7017370207
"stringUnit": {
7017470208
"state": "translated",
70175-
"value": "Otwórz %1$@ #%2$lld"
70209+
"value": "Otwórz %1$@ #%2$@"
7017670210
}
7017770211
},
7017870212
"ru": {
7017970213
"stringUnit": {
7018070214
"state": "translated",
70181-
"value": "Открыть %1$@ #%2$lld"
70215+
"value": "Открыть %1$@ #%2$@"
7018270216
}
7018370217
},
7018470218
"bs": {
7018570219
"stringUnit": {
7018670220
"state": "translated",
70187-
"value": "Otvori %1$@ #%2$lld"
70221+
"value": "Otvori %1$@ #%2$@"
7018870222
}
7018970223
},
7019070224
"ar": {
7019170225
"stringUnit": {
7019270226
"state": "translated",
70193-
"value": "فتح %1$@ #%2$lld"
70227+
"value": "فتح %1$@ #%2$@"
7019470228
}
7019570229
},
7019670230
"nb": {
7019770231
"stringUnit": {
7019870232
"state": "translated",
70199-
"value": "Åpne %1$@ #%2$lld"
70233+
"value": "Åpne %1$@ #%2$@"
7020070234
}
7020170235
},
7020270236
"pt-BR": {
7020370237
"stringUnit": {
7020470238
"state": "translated",
70205-
"value": "Abrir %1$@ #%2$lld"
70239+
"value": "Abrir %1$@ #%2$@"
7020670240
}
7020770241
},
7020870242
"th": {
7020970243
"stringUnit": {
7021070244
"state": "translated",
70211-
"value": "เปิด %1$@ #%2$lld"
70245+
"value": "เปิด %1$@ #%2$@"
7021270246
}
7021370247
},
7021470248
"tr": {
7021570249
"stringUnit": {
7021670250
"state": "translated",
70217-
"value": "%1$@ #%2$lld Aç"
70251+
"value": "%1$@ #%2$@ Aç"
7021870252
}
7021970253
},
7022070254
"uk": {
7022170255
"stringUnit": {
7022270256
"state": "translated",
70223-
"value": "Відкрити %1$@ #%2$lld"
70257+
"value": "Відкрити %1$@ #%2$@"
7022470258
}
7022570259
}
7022670260
}

0 commit comments

Comments
 (0)