Add cmux omo command for oh-my-openagent integration#2087
Add cmux omo command for oh-my-openagent integration#2087lawrencecchen merged 20 commits intomainfrom
cmux omo command for oh-my-openagent integration#2087Conversation
Same pattern as `cmux claude-teams`: creates a tmux shim so oh-my-openagent's TmuxSessionManager spawns agents as native cmux splits instead of tmux panes. Sets TMUX/TMUX_PANE env vars, prepends shim to PATH, and execs into opencode. Closes #2085
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as cmux CLI
participant FS as Filesystem
participant Installer as Installer (bun/npm)
participant OpenCode as opencode
User->>CLI: run `cmux omo [args]` or `cmux omo --help`
CLI-->>User: show help (if requested)
CLI->>FS: ensure shadow config & merge/ensure plugin in opencode.json
CLI->>FS: create symlinks and tmux shim under ~/.cmuxterm
CLI->>Installer: install `oh-my-opencode` if missing (bun/npm)
CLI->>CLI: set PATH, TMUX, TMUX_PANE, TERM, CMUX_* and OPENCODE_CONFIG_DIR
CLI->>OpenCode: execv/execvp opencode with forwarded args
OpenCode-->>CLI: (on exec failure) process returns error -> CLI reports to User
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds
Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant cmux as cmux CLI
participant Shim as ~/.cmuxterm/omo-bin/tmux
participant OC as opencode
participant OMA as oh-my-openagent
participant CmuxSocket as cmux socket
User->>cmux: cmux omo [args...]
cmux->>cmux: createOMOShimDirectory()
cmux->>Shim: write shim script (if changed)
cmux->>CmuxSocket: claudeTeamsFocusedContext() [optional]
CmuxSocket-->>cmux: workspaceId, paneHandle, etc.
cmux->>cmux: configureOMOEnvironment()<br/>setenv PATH=shim:..., TMUX, TMUX_PANE,<br/>CMUX_SOCKET_PATH, CMUX_OMO_CMUX_BIN
cmux->>OC: execv(opencode, args)
OC->>OMA: spawns background agents
OMA->>Shim: tmux new-window / split-window
Shim->>cmux: cmux __tmux-compat [tmux-args]
cmux->>CmuxSocket: translate to cmux workspace/split ops
CmuxSocket-->>cmux: new split created
Reviews (1): Last reviewed commit: "Add `cmux omo` command for OpenCode + oh..." | Re-trigger Greptile |
| private func resolveOpenCodeExecutable(searchPath: String?) -> String? { | ||
| let entries = searchPath?.split(separator: ":").map(String.init) ?? [] | ||
| for entry in entries where !entry.isEmpty { | ||
| let candidate = URL(fileURLWithPath: entry, isDirectory: true) | ||
| .appendingPathComponent("opencode", isDirectory: false) | ||
| .path | ||
| guard FileManager.default.isExecutableFile(atPath: candidate) else { continue } | ||
| return candidate | ||
| } | ||
| return nil |
There was a problem hiding this comment.
Missing wrapper-detection guard
resolveClaudeExecutable calls isCmuxClaudeWrapper(at:) to skip any claude binary that is already a cmux shim — preventing an infinite exec loop if a wrapper is installed on the user's system. resolveOpenCodeExecutable has no analogous check. There is currently no cmux-opencode wrapper, so this can't cause an actual loop today, but adding the guard now keeps the two resolvers structurally consistent and future-proofs against an opencode shim being added later.
| private func resolveOpenCodeExecutable(searchPath: String?) -> String? { | |
| let entries = searchPath?.split(separator: ":").map(String.init) ?? [] | |
| for entry in entries where !entry.isEmpty { | |
| let candidate = URL(fileURLWithPath: entry, isDirectory: true) | |
| .appendingPathComponent("opencode", isDirectory: false) | |
| .path | |
| guard FileManager.default.isExecutableFile(atPath: candidate) else { continue } | |
| return candidate | |
| } | |
| return nil | |
| private func resolveOpenCodeExecutable(searchPath: String?) -> String? { | |
| let entries = searchPath?.split(separator: ":").map(String.init) ?? [] | |
| for entry in entries where !entry.isEmpty { | |
| let candidate = URL(fileURLWithPath: entry, isDirectory: true) | |
| .appendingPathComponent("opencode", isDirectory: false) | |
| .path | |
| guard FileManager.default.isExecutableFile(atPath: candidate) else { continue } | |
| guard !isCmuxOpenCodeWrapper(at: candidate) else { continue } | |
| return candidate | |
| } | |
| return nil | |
| } |
(where isCmuxOpenCodeWrapper would mirror isCmuxClaudeWrapper with an opencode-specific sentinel string)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 697f4ba497
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
| exec "${CMUX_OMO_CMUX_BIN:-cmux}" __tmux-compat "$@" | ||
| """ |
There was a problem hiding this comment.
Provide tmux pane metric fields for OMO compatibility
The new omo wrapper routes every tmux call through cmux __tmux-compat, but this compat path’s list-panes -F renderer only fills a small context and drops unknown placeholders, while OMO’s tmux session manager requests fields like #{pane_width}, #{pane_height}, #{pane_left}, #{pane_top}, #{pane_active}, and window dimensions to parse pane state. Because those placeholders are stripped, pane-state parsing fails and OMO cannot reliably spawn/track subagent panes, so cmux omo does not deliver the advertised native split workflow.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
CLI/cmux.swift (1)
9416-9540: Extract the tmux-shim launcher instead of copyingclaude-teams.
CMUXCLI.createOMOShimDirectory(),CMUXCLI.configureOMOEnvironment(processEnvironment:shimDirectory:executablePath:socketPath:explicitPassword:focusedContext:), andCMUXCLI.runOMO(commandArgs:socketPath:explicitPassword:)are now almost a line-for-line copy of theclaude-teamspath. Keeping two copies of the socket/env/shim/exec flow makes future fixes easy to land in one command and miss in the other.Refactor sketch
+ private struct TmuxShimLaunchConfig { + let shimDirectoryName: String + let shimBinaryEnvKey: String + let commandName: String + let resolvedExecutablePath: String? + let extraEnvironment: [String: String] + let launchArguments: [String] + } + + private func runTmuxShimLauncher( + _ config: TmuxShimLaunchConfig, + socketPath: String, + explicitPassword: String? + ) throws { + // shared: create shim directory, resolve focused context, + // configure environment, and exec the target binary + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CLI/cmux.swift` around lines 9416 - 9540, The OMO path duplicates the claude-teams shim/socket/env/exec flow; extract the shared tmux-shim launcher into reusable helpers and call them from both OMO and claude-teams to remove duplication. Create common functions (e.g. createShimDirectory(sharedScriptName:), configureShimEnvironment(processEnvironment:shimDirectory:executablePath:socketPath:explicitPassword:focusedContext:additionalVars:), and runShim(commandArgs:launchName:useExecvIfPathExists:)) that encapsulate the directory/script creation, posix permission setting, PATH/TMUX/TMUX_PANE/TERM/CMUX_SOCKET* env wiring, and execv/execvp logic (including openCode resolution) while allowing callers to pass small per-path differences (like CMUX_OMO_* env names, fake TMUX value and focusedContext handling). Replace CMUXCLI.createOMOShimDirectory, configureOMOEnvironment, and runOMO to delegate to these shared helpers, preserving all existing behaviors (script contents, permissions, CMUX_OMO_CMUX_BIN, CMUX_WORKSPACE_ID/CMUX_SURFACE_ID handling, and explicitPassword checks).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLI/cmux.swift`:
- Around line 1414-1417: The guard condition currently excluding "omo" prevents
the help branch from showing omo usage; remove the `command != "omo"` check from
the if-condition so that when command == "omo" and commandArgs contains "--help"
or "-h" the code falls into the usage display path (invoke
CMUXCLI.subcommandUsage("omo") / use cli.omo.usage) instead of falling through
to CMUXCLI.runOMO(commandArgs:socketPath:explicitPassword:).
---
Nitpick comments:
In `@CLI/cmux.swift`:
- Around line 9416-9540: The OMO path duplicates the claude-teams
shim/socket/env/exec flow; extract the shared tmux-shim launcher into reusable
helpers and call them from both OMO and claude-teams to remove duplication.
Create common functions (e.g. createShimDirectory(sharedScriptName:),
configureShimEnvironment(processEnvironment:shimDirectory:executablePath:socketPath:explicitPassword:focusedContext:additionalVars:),
and runShim(commandArgs:launchName:useExecvIfPathExists:)) that encapsulate the
directory/script creation, posix permission setting,
PATH/TMUX/TMUX_PANE/TERM/CMUX_SOCKET* env wiring, and execv/execvp logic
(including openCode resolution) while allowing callers to pass small per-path
differences (like CMUX_OMO_* env names, fake TMUX value and focusedContext
handling). Replace CMUXCLI.createOMOShimDirectory, configureOMOEnvironment, and
runOMO to delegate to these shared helpers, preserving all existing behaviors
(script contents, permissions, CMUX_OMO_CMUX_BIN,
CMUX_WORKSPACE_ID/CMUX_SURFACE_ID handling, and explicitPassword checks).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2de362e5-30b0-4153-829b-45662030ceaa
📒 Files selected for processing (2)
CLI/cmux.swiftResources/Localizable.xcstrings
Before launching opencode, cmux omo now: - Checks if oh-my-opencode is registered in ~/.config/opencode/opencode.json - If not, creates/updates the config with the plugin entry - Checks if the npm package is installed in node_modules - If not, runs bun add (or npm install) to install it - Then proceeds with tmux shim setup and exec
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 60aca2af5e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let data = try? Data(contentsOf: jsonURL), | ||
| let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { | ||
| config = existing | ||
| } else { | ||
| config = [:] |
There was a problem hiding this comment.
Abort when opencode.json cannot be parsed
omoRegisterPlugin silently treats any decode failure as an empty config and then writes opencode.json back, which can erase existing user settings. If ~/.config/opencode/opencode.json is temporarily malformed (or uses syntax JSONSerialization rejects), running cmux omo will overwrite it with only the new plugin array. This is destructive data loss; parse errors should fail fast instead of resetting the config.
Useful? React with 👍 / 👎.
| process.standardError = stderr | ||
|
|
||
| FileHandle.standardError.write("Installing oh-my-opencode plugin...\n".data(using: .utf8)!) | ||
| try process.run() | ||
| process.waitUntilExit() |
There was a problem hiding this comment.
Drain installer pipes before waiting for process exit
The install path pipes both stdout/stderr and then calls waitUntilExit() without consuming either stream first. During noisy bun add/npm install runs, pipe buffers can fill and block the child process on write, which leaves cmux omo hanging indefinitely on first-time setup. Consume the pipes concurrently (or inherit terminal stdio) before waiting.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
CLI/cmux.swift (1)
1414-1417:⚠️ Potential issue | 🟠 Major
cmux omo --helpstill falls into the side-effecting launcher.Line 1416 still excludes
omofrom the early help path, socmux omo --helpreachesrunOMO()instead of printing thecli.omo.usagetext. With the new bootstrap flow, that now also mutates~/.config/opencodeand may runbun add/npm installjust to ask for help.Suggested fix
if command != "__tmux-compat", command != "claude-teams", - command != "omo", (commandArgs.contains("--help") || commandArgs.contains("-h")) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CLI/cmux.swift` around lines 1414 - 1417, The check currently excludes "omo" from the early-help branch so "cmux omo --help" falls through to runOMO() and causes unwanted side effects; update the condition handling around command/commandArgs so that "omo" is treated like other helpable commands (either remove the "command != \"omo\"" exclusion from the if that prints cli.omo.usage or add an explicit short-circuit: if command == "omo" and commandArgs contains "--help" or "-h" then print cli.omo.usage and return before calling runOMO()), ensuring runOMO() is not invoked when only help was requested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLI/cmux.swift`:
- Around line 9461-9468: omoPluginIsRegistered currently recognizes
version-pinned entries in opencode.json but install checks and bootstrap logic
still only look for an unversioned node_modules/oh-my-opencode and always run
installs without the pinned spec; update the install/verification paths to honor
version-pinned plugin strings by (1) parsing the plugin entry (e.g.,
"oh-my-opencode@1.2.3") instead of using Self.omoPluginName alone, (2) checking
the installed package version by either looking for
node_modules/<package>/package.json and comparing its "name" and "version" or by
checking node_modules/<package>@<version> if your packaging system supports that
layout, and (3) when invoking installers (bun add / npm install) pass the exact
plugin spec from opencode.json so pinned versions are installed; apply the same
change to the other related checks referenced near the other occurrences of the
bootstrap/install logic.
- Around line 9476-9494: The code currently treats a JSON parse failure as an
empty config and will overwrite an unreadable opencode.json with only the plugin
entry; change the logic in the block that reads Data(contentsOf: jsonURL) /
JSONSerialization.jsonObject to differentiate "file missing" from "file present
but unreadable": if Data(contentsOf: jsonURL) throws because the file is
missing, initialize config = [:] and continue, but if the file exists and
JSONSerialization.jsonObject throws (i.e., parsing fails), throw or return an
error instead of assigning config = [:]; update handling around jsonURL, config,
plugins and the write using Self.omoPluginName so we only bootstrap when the
file is absent and avoid clobbering an unreadable existing file.
- Around line 9521-9532: The child process may deadlock because stdout/stderr
Pipes (stdout, stderr) are not drained before waitUntilExit(); update the code
in the block that configures and runs the Process (the process variable and its
standardOutput/standardError assignment) to drain both pipes concurrently—either
by attaching readabilityHandler closures to stdout.fileHandleForReading and
stderr.fileHandleForReading that accumulate output into buffers and clear the
handlers before calling waitUntilExit(), or by setting
process.standardOutput/process.standardError to FileHandle.nullDevice if you
don't need output; ensure you still capture any stderr text used in the thrown
CLIError by collecting it from the handler buffer after the process exits.
---
Duplicate comments:
In `@CLI/cmux.swift`:
- Around line 1414-1417: The check currently excludes "omo" from the early-help
branch so "cmux omo --help" falls through to runOMO() and causes unwanted side
effects; update the condition handling around command/commandArgs so that "omo"
is treated like other helpable commands (either remove the "command != \"omo\""
exclusion from the if that prints cli.omo.usage or add an explicit
short-circuit: if command == "omo" and commandArgs contains "--help" or "-h"
then print cli.omo.usage and return before calling runOMO()), ensuring runOMO()
is not invoked when only help was requested.
| private func omoPluginIsRegistered(configDir: URL) -> Bool { | ||
| let jsonURL = configDir.appendingPathComponent("opencode.json") | ||
| guard let data = try? Data(contentsOf: jsonURL), | ||
| let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any], | ||
| let plugins = obj["plugin"] as? [String] else { | ||
| return false | ||
| } | ||
| return plugins.contains { $0 == Self.omoPluginName || $0.hasPrefix("\(Self.omoPluginName)@") } |
There was a problem hiding this comment.
Honor version-pinned plugin entries during install checks.
Line 9468 already treats oh-my-opencode@... as a valid configured plugin, but the bootstrap path only checks for an unversioned node_modules/oh-my-opencode directory and always runs bun add / npm install oh-my-opencode. If a user pins a version in opencode.json, cmux omo can silently drift to a different package than the config asks for.
Also applies to: 9497-9503, 9511-9518, 9550-9558
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CLI/cmux.swift` around lines 9461 - 9468, omoPluginIsRegistered currently
recognizes version-pinned entries in opencode.json but install checks and
bootstrap logic still only look for an unversioned node_modules/oh-my-opencode
and always run installs without the pinned spec; update the install/verification
paths to honor version-pinned plugin strings by (1) parsing the plugin entry
(e.g., "oh-my-opencode@1.2.3") instead of using Self.omoPluginName alone, (2)
checking the installed package version by either looking for
node_modules/<package>/package.json and comparing its "name" and "version" or by
checking node_modules/<package>@<version> if your packaging system supports that
layout, and (3) when invoking installers (bun add / npm install) pass the exact
plugin spec from opencode.json so pinned versions are installed; apply the same
change to the other related checks referenced near the other occurrences of the
bootstrap/install logic.
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="CLI/cmux.swift">
<violation number="1" location="CLI/cmux.swift:9480">
P2: Treating parse failures as an empty config can overwrite an existing `opencode.json` and erase user settings.</violation>
<violation number="2" location="CLI/cmux.swift:9521">
P1: Piping bun/npm output without draining it can deadlock the install step and hang `cmux omo`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Instead of writing directly to ~/.config/opencode/opencode.json, cmux omo now creates a shadow config at ~/.cmuxterm/omo-config/ that layers oh-my-opencode on top of the user's existing config. Symlinks node_modules, package.json, bun.lock, and plugin config from the original dir. Sets OPENCODE_CONFIG_DIR to the shadow directory. Running plain `opencode` remains unaffected.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b43d33f21
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let attrs = try? fm.attributesOfItem(atPath: shadowNodeModules.path), | ||
| attrs[.type] as? FileAttributeType == .typeSymbolicLink { | ||
| let target = try? fm.destinationOfSymbolicLink(atPath: shadowNodeModules.path) | ||
| if target != userNodeModules.path { | ||
| try? fm.removeItem(at: shadowNodeModules) |
There was a problem hiding this comment.
Replace stale shadow node_modules with user symlink
When ~/.cmuxterm/omo-config/node_modules already exists as a real directory (for example, from an earlier run that installed into the shadow config), this block only cleans up mismatched symlinks and never replaces a directory. If the user later gets ~/.config/opencode/node_modules, cmux omo keeps using the stale shadow directory and misses plugins/packages installed in the real OpenCode config, so plugin resolution diverges from the user environment.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="CLI/cmux.swift">
<violation number="1" location="CLI/cmux.swift:9519">
P2: Handle the case where `shadowNodeModules` already exists as a real directory. Right now only mismatched symlinks are removed, so a stale directory is kept and package resolution can drift from the user's OpenCode environment.</violation>
<violation number="2" location="CLI/cmux.swift:9569">
P1: Avoid piping child stdout here (or drain it asynchronously). Waiting for exit before consuming piped output can deadlock during `bun`/`npm` install and hang `cmux omo`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| let userNodeModules = userDir.appendingPathComponent("node_modules") | ||
| if fm.fileExists(atPath: userNodeModules.path) { | ||
| // Remove stale symlink or directory if it exists | ||
| if let attrs = try? fm.attributesOfItem(atPath: shadowNodeModules.path), |
There was a problem hiding this comment.
P2: Handle the case where shadowNodeModules already exists as a real directory. Right now only mismatched symlinks are removed, so a stale directory is kept and package resolution can drift from the user's OpenCode environment.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CLI/cmux.swift, line 9519:
<comment>Handle the case where `shadowNodeModules` already exists as a real directory. Right now only mismatched symlinks are removed, so a stale directory is kept and package resolution can drift from the user's OpenCode environment.</comment>
<file context>
@@ -9491,72 +9509,83 @@ struct CMUXCLI {
+ let userNodeModules = userDir.appendingPathComponent("node_modules")
+ if fm.fileExists(atPath: userNodeModules.path) {
+ // Remove stale symlink or directory if it exists
+ if let attrs = try? fm.attributesOfItem(atPath: shadowNodeModules.path),
+ attrs[.type] as? FileAttributeType == .typeSymbolicLink {
+ let target = try? fm.destinationOfSymbolicLink(atPath: shadowNodeModules.path)
</file context>
…opencode pages Adds sectioned sidebar navigation to the docs site. The new Agent Integrations section contains separate pages for cmux claude-teams and cmux omo, documenting usage, tmux shim mechanics, directory layout, environment variables, and the shadow config approach. Both pages include a nightly-only warning. Full English and Japanese translations, nav item keys added to all 19 locales.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 763d7b14c3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Need to install into the real user config dir so the symlink picks it up | ||
| let installDir = fm.fileExists(atPath: userNodeModules.path) ? userDir : shadowDir | ||
| // If installing into shadow dir, remove the symlink first |
There was a problem hiding this comment.
Install oh-my-opencode only in shadow config
When ~/.config/opencode/node_modules already exists, this selects installDir as userDir, so bun add/npm install runs against the user's real OpenCode config and mutates their package.json/lockfiles and dependencies. That breaks the command’s own “shadow config without modifying original config” behavior and can unexpectedly alter a user’s existing OpenCode setup on first cmux omo run.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/app/`[locale]/components/docs-pager.tsx:
- Around line 11-13: The code computes index = flat.findIndex(...) which can be
-1; update the logic in the docs-pager component to guard that case before
computing prev/next: after computing index, if index === -1 return early (or set
both prev and next to null) so you don't treat flat[-1] as a valid neighbor;
adjust the component's return path accordingly to avoid rendering incorrect
links when pathname is not found.
In `@web/messages/de.json`:
- Around line 554-556: The German locale (de.json) adds navigation labels
"claudeCodeTeams" and "ohMyOpenCode" but is missing the full translation
namespaces docs.claudeCodeTeams and docs.ohMyOpenCode used by the feature pages;
add the complete namespace objects (title, meta, headings, usage sections, and
all keys present in en.json/ja.json) into de.json (or stub them with English
fallback values) so page rendering can resolve the ~40 missing keys; locate the
canonical structures in en.json or ja.json and replicate their keys/structure
into de.json under docs.claudeCodeTeams and docs.ohMyOpenCode, keeping key names
identical.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bdcc47c5-85c6-4bff-9a3a-08ebd54ea91f
📒 Files selected for processing (25)
CLI/cmux.swiftweb/app/[locale]/components/docs-nav-items.tsweb/app/[locale]/components/docs-pager.tsxweb/app/[locale]/components/docs-sidebar.tsxweb/app/[locale]/docs/agent-integrations/claude-code-teams/page.tsxweb/app/[locale]/docs/agent-integrations/oh-my-opencode/page.tsxweb/messages/ar.jsonweb/messages/bs.jsonweb/messages/da.jsonweb/messages/de.jsonweb/messages/en.jsonweb/messages/es.jsonweb/messages/fr.jsonweb/messages/it.jsonweb/messages/ja.jsonweb/messages/km.jsonweb/messages/ko.jsonweb/messages/no.jsonweb/messages/pl.jsonweb/messages/pt-BR.jsonweb/messages/ru.jsonweb/messages/th.jsonweb/messages/tr.jsonweb/messages/zh-CN.jsonweb/messages/zh-TW.json
✅ Files skipped from review due to trivial changes (16)
- web/messages/th.json
- web/messages/tr.json
- web/messages/da.json
- web/messages/es.json
- web/messages/pl.json
- web/messages/ru.json
- web/messages/fr.json
- web/messages/it.json
- web/messages/pt-BR.json
- web/messages/en.json
- web/messages/zh-CN.json
- web/messages/km.json
- web/messages/no.json
- web/messages/ar.json
- web/messages/ko.json
- web/messages/ja.json
🚧 Files skipped from review as they are similar to previous changes (1)
- CLI/cmux.swift
| const index = flat.findIndex((item) => item.href === pathname); | ||
| const prev = index > 0 ? flat[index - 1] : null; | ||
| const next = index < flat.length - 1 ? flat[index + 1] : null; |
There was a problem hiding this comment.
Guard the -1 index case before computing neighbors.
Line 11 can return -1; then Line 13 incorrectly sets next to the first doc item. Add an early return when the path is not in the flattened nav.
Suggested fix
const flat = flatNavItems(navItems);
const index = flat.findIndex((item) => item.href === pathname);
+ if (index === -1) return null;
const prev = index > 0 ? flat[index - 1] : null;
const next = index < flat.length - 1 ? flat[index + 1] : null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const index = flat.findIndex((item) => item.href === pathname); | |
| const prev = index > 0 ? flat[index - 1] : null; | |
| const next = index < flat.length - 1 ? flat[index + 1] : null; | |
| const flat = flatNavItems(navItems); | |
| const index = flat.findIndex((item) => item.href === pathname); | |
| if (index === -1) return null; | |
| const prev = index > 0 ? flat[index - 1] : null; | |
| const next = index < flat.length - 1 ? flat[index + 1] : null; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/app/`[locale]/components/docs-pager.tsx around lines 11 - 13, The code
computes index = flat.findIndex(...) which can be -1; update the logic in the
docs-pager component to guard that case before computing prev/next: after
computing index, if index === -1 return early (or set both prev and next to
null) so you don't treat flat[-1] as a valid neighbor; adjust the component's
return path accordingly to avoid rendering incorrect links when pathname is not
found.
There was a problem hiding this comment.
12 issues found across 24 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/messages/ar.json">
<violation number="1" location="web/messages/ar.json:554">
P3: `agentIntegrations` is left untranslated in the Arabic locale, causing mixed-language navigation text.</violation>
</file>
<file name="web/messages/it.json">
<violation number="1" location="web/messages/it.json:554">
P3: These new nav labels are still in English in the Italian locale file. Provide Italian translations so the UI doesn’t show untranslated strings.</violation>
</file>
<file name="web/messages/da.json">
<violation number="1" location="web/messages/da.json:554">
P3: Localize this Danish nav label instead of leaving it in English to avoid mixed-language UI.</violation>
</file>
<file name="web/messages/tr.json">
<violation number="1" location="web/messages/tr.json:554">
P3: Translate this new Turkish nav label; leaving it in English causes mixed-language UI in the Turkish locale.</violation>
</file>
<file name="web/messages/es.json">
<violation number="1" location="web/messages/es.json:554">
P3: Translate this new nav label in the Spanish locale to avoid mixed-language navigation text.</violation>
</file>
<file name="web/messages/ko.json">
<violation number="1" location="web/messages/ko.json:554">
P3: Localize this new Korean nav label instead of leaving it in English.</violation>
</file>
<file name="web/messages/pt-BR.json">
<violation number="1" location="web/messages/pt-BR.json:554">
P3: Localize this new pt-BR navigation label instead of leaving it in English.</violation>
</file>
<file name="web/messages/zh-TW.json">
<violation number="1" location="web/messages/zh-TW.json:554">
P3: Translate this zh-TW navigation label instead of leaving it in English.</violation>
</file>
<file name="web/messages/de.json">
<violation number="1" location="web/messages/de.json:554">
P3: Localize this new German nav label; it is currently left in English and creates mixed-language navigation.</violation>
</file>
<file name="web/messages/no.json">
<violation number="1" location="web/messages/no.json:554">
P3: `agentIntegrations` is left in English in the Norwegian locale file; localize this label to keep navigation language consistent.</violation>
</file>
<file name="web/messages/zh-CN.json">
<violation number="1" location="web/messages/zh-CN.json:554">
P3: Translate this new zh-CN navigation label to Chinese to keep locale consistency.</violation>
</file>
<file name="web/messages/pl.json">
<violation number="1" location="web/messages/pl.json:554">
P3: These new navigation labels are left in English in the Polish locale. Provide Polish translations so the UI stays localized.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| "apiReference": "مرجع الواجهة البرمجية", | ||
| "browserAutomation": "أتمتة المتصفح", | ||
| "notifications": "الإشعارات", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: agentIntegrations is left untranslated in the Arabic locale, causing mixed-language navigation text.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/ar.json, line 554:
<comment>`agentIntegrations` is left untranslated in the Arabic locale, causing mixed-language navigation text.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "مرجع الواجهة البرمجية",
"browserAutomation": "أتمتة المتصفح",
"notifications": "الإشعارات",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "تكاملات الوكلاء", |
| "agentIntegrations": "Agent Integrations", | ||
| "claudeCodeTeams": "Claude Code Teams", | ||
| "ohMyOpenCode": "oh-my-opencode", |
There was a problem hiding this comment.
P3: These new nav labels are still in English in the Italian locale file. Provide Italian translations so the UI doesn’t show untranslated strings.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/it.json, line 554:
<comment>These new nav labels are still in English in the Italian locale file. Provide Italian translations so the UI doesn’t show untranslated strings.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "Riferimento API",
"browserAutomation": "Automazione del browser",
"notifications": "Notifiche",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "claudeCodeTeams": "Claude Code Teams", | |
| "ohMyOpenCode": "oh-my-opencode", | |
| "agentIntegrations": "Integrazioni agenti", | |
| "claudeCodeTeams": "Team Claude Code", | |
| "ohMyOpenCode": "oh-my-opencode", |
| "apiReference": "API-reference", | ||
| "browserAutomation": "Browserautomatisering", | ||
| "notifications": "Notifikationer", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Localize this Danish nav label instead of leaving it in English to avoid mixed-language UI.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/da.json, line 554:
<comment>Localize this Danish nav label instead of leaving it in English to avoid mixed-language UI.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API-reference",
"browserAutomation": "Browserautomatisering",
"notifications": "Notifikationer",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Agentintegrationer", |
| "apiReference": "API Referansı", | ||
| "browserAutomation": "Tarayıcı Otomasyonu", | ||
| "notifications": "Bildirimler", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Translate this new Turkish nav label; leaving it in English causes mixed-language UI in the Turkish locale.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/tr.json, line 554:
<comment>Translate this new Turkish nav label; leaving it in English causes mixed-language UI in the Turkish locale.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API Referansı",
"browserAutomation": "Tarayıcı Otomasyonu",
"notifications": "Bildirimler",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Ajan Entegrasyonları", |
| "apiReference": "Referencia de API", | ||
| "browserAutomation": "Automatización del navegador", | ||
| "notifications": "Notificaciones", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Translate this new nav label in the Spanish locale to avoid mixed-language navigation text.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/es.json, line 554:
<comment>Translate this new nav label in the Spanish locale to avoid mixed-language navigation text.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "Referencia de API",
"browserAutomation": "Automatización del navegador",
"notifications": "Notificaciones",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Integraciones de agentes", |
| "apiReference": "API 參考", | ||
| "browserAutomation": "瀏覽器自動化", | ||
| "notifications": "通知", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Translate this zh-TW navigation label instead of leaving it in English.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/zh-TW.json, line 554:
<comment>Translate this zh-TW navigation label instead of leaving it in English.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API 參考",
"browserAutomation": "瀏覽器自動化",
"notifications": "通知",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Agent 整合", |
| "apiReference": "API-Referenz", | ||
| "browserAutomation": "Browser-Automatisierung", | ||
| "notifications": "Benachrichtigungen", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Localize this new German nav label; it is currently left in English and creates mixed-language navigation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/de.json, line 554:
<comment>Localize this new German nav label; it is currently left in English and creates mixed-language navigation.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API-Referenz",
"browserAutomation": "Browser-Automatisierung",
"notifications": "Benachrichtigungen",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Agent-Integrationen", |
| "apiReference": "API-referanse", | ||
| "browserAutomation": "Nettleserautomatisering", | ||
| "notifications": "Varsler", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: agentIntegrations is left in English in the Norwegian locale file; localize this label to keep navigation language consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/no.json, line 554:
<comment>`agentIntegrations` is left in English in the Norwegian locale file; localize this label to keep navigation language consistent.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API-referanse",
"browserAutomation": "Nettleserautomatisering",
"notifications": "Varsler",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "Agentintegrasjoner", |
| "apiReference": "API 参考", | ||
| "browserAutomation": "浏览器自动化", | ||
| "notifications": "通知", | ||
| "agentIntegrations": "Agent Integrations", |
There was a problem hiding this comment.
P3: Translate this new zh-CN navigation label to Chinese to keep locale consistency.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/zh-CN.json, line 554:
<comment>Translate this new zh-CN navigation label to Chinese to keep locale consistency.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "API 参考",
"browserAutomation": "浏览器自动化",
"notifications": "通知",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "agentIntegrations": "智能体集成", |
| "agentIntegrations": "Agent Integrations", | ||
| "claudeCodeTeams": "Claude Code Teams", |
There was a problem hiding this comment.
P3: These new navigation labels are left in English in the Polish locale. Provide Polish translations so the UI stays localized.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/messages/pl.json, line 554:
<comment>These new navigation labels are left in English in the Polish locale. Provide Polish translations so the UI stays localized.</comment>
<file context>
@@ -551,6 +551,9 @@
"apiReference": "Dokumentacja API",
"browserAutomation": "Automatyzacja przeglądarki",
"notifications": "Powiadomienia",
+ "agentIntegrations": "Agent Integrations",
+ "claudeCodeTeams": "Claude Code Teams",
+ "ohMyOpenCode": "oh-my-opencode",
</file context>
| "agentIntegrations": "Agent Integrations", | |
| "claudeCodeTeams": "Claude Code Teams", | |
| "agentIntegrations": "Integracje agentów", | |
| "claudeCodeTeams": "Zespoły Claude Code", |
- cmux omo now writes tmux.enabled=true to the shadow oh-my-opencode.json config. Without this, oh-my-openagent's TmuxSessionManager won't spawn visual panes even though $TMUX is set (the config defaults to false). - Nightly warnings now link to /nightly instead of generic text. - Added "What you get" section to oh-my-opencode docs explaining the visual pane behavior (auto-layout, idle cleanup, queueing). - Added tmux.enabled step to first-run and how-it-works sections.
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="CLI/cmux.swift">
<violation number="1" location="CLI/cmux.swift:9590">
P2: This path only reads/writes `oh-my-opencode.json`, so users with config in `oh-my-opencode.jsonc` can lose existing plugin settings when tmux mode is enforced.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| // Ensure tmux mode is enabled in oh-my-opencode 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") |
There was a problem hiding this comment.
P2: This path only reads/writes oh-my-opencode.json, so users with config in oh-my-opencode.jsonc can lose existing plugin settings when tmux mode is enforced.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CLI/cmux.swift, line 9590:
<comment>This path only reads/writes `oh-my-opencode.json`, so users with config in `oh-my-opencode.jsonc` can lose existing plugin settings when tmux mode is enforced.</comment>
<file context>
@@ -9584,6 +9584,39 @@ struct CMUXCLI {
+ // Ensure tmux mode is enabled in oh-my-opencode 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")
+ var omoConfig: [String: Any]
+ if let data = try? Data(contentsOf: omoConfigURL),
</file context>
- Keep upstream main-vertical layout anchoring from #2119 - Keep upstream focus param (v2Bool) instead of no_focus - Combine with our -d flag handling: -d sets focus=false - Include customCommands nav item from main
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="CLI/cmux.swift">
<violation number="1" location="CLI/cmux.swift:9844">
P2: OPENCODE_PORT is hardcoded to 4096 even when the user supplies a different --port. That makes oh-my-opencode attach to the wrong API port. Derive OPENCODE_PORT from the effective args or avoid setting it when --port is provided.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } | ||
| unsetenv("TERM_PROGRAM") | ||
| // Tell oh-my-opencode the API server port so subagent attach works | ||
| if processEnvironment["OPENCODE_PORT"] == nil { |
There was a problem hiding this comment.
P2: OPENCODE_PORT is hardcoded to 4096 even when the user supplies a different --port. That makes oh-my-opencode attach to the wrong API port. Derive OPENCODE_PORT from the effective args or avoid setting it when --port is provided.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CLI/cmux.swift, line 9844:
<comment>OPENCODE_PORT is hardcoded to 4096 even when the user supplies a different --port. That makes oh-my-opencode attach to the wrong API port. Derive OPENCODE_PORT from the effective args or avoid setting it when --port is provided.</comment>
<file context>
@@ -9840,6 +9840,10 @@ struct CMUXCLI {
}
unsetenv("TERM_PROGRAM")
+ // Tell oh-my-opencode the API server port so subagent attach works
+ if processEnvironment["OPENCODE_PORT"] == nil {
+ setenv("OPENCODE_PORT", "4096", 1)
+ }
</file context>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1804a315cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let created = try client.sendV2(method: "surface.split", params: [ | ||
| "workspace_id": target.workspaceId, | ||
| "surface_id": target.surfaceId, | ||
| "direction": direction, | ||
| "focus": false | ||
| "focus": focusNewPane |
There was a problem hiding this comment.
Preserve pane focus for tmux split-window compat path
This change makes __tmux-compat split-window focus the newly created pane whenever -d is absent, which means background agent pane creation can unexpectedly move the user’s current in-app focus. That contradicts the socket focus policy in /workspace/cmux/AGENTS.md (only explicit focus-intent commands should mutate focus, and this path is not one of them), so this introduces focus-stealing behavior during normal cmux omo/agent workflows.
Useful? React with 👍 / 👎.
| if !commandArgs.contains("--port") { | ||
| effectiveArgs.append("--port") | ||
| effectiveArgs.append("4096") |
There was a problem hiding this comment.
Honor inline
--port= when deciding default OMO port
The default-port injection only checks for an argument exactly equal to --port, so cmux omo --port=1234 is misdetected as “no port provided” and --port 4096 is appended anyway. That produces conflicting port flags and can force OpenCode onto the wrong API port; this check should also recognize inline forms like --port=... (and any other supported synonyms) before appending defaults.
Useful? React with 👍 / 👎.
When oh-my-openagent spawns agent panes, it calls select-layout main-vertical after each split to redistribute panes evenly, then resize-pane -x <columns> to set the main pane width. Both were previously no-ops, causing cascading uneven splits. Server side: - Add workspace.equalize_splits v2 API that calls the existing TabManager.equalizeSplits (sets all dividers to 0.5) CLI side: - select-layout now calls workspace.equalize_splits before tracking main-vertical state - resize-pane -x <columns> without directional flags now computes the pixel delta from current to desired width and resizes accordingly
The previous equalize set all dividers to 0.5, which in a right- recursive binary tree (from successive splits) gives 50/25/12.5/6.25% instead of equal sizes. New algorithm counts leaf panes on each side of each split and sets the divider to N_left / (N_left + N_right). For 5 panes in a chain: 1/5, 1/4, 1/3, 1/2, giving each pane exactly 20%.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e5799e58e1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if processEnvironment["OPENCODE_PORT"] == nil { | ||
| setenv("OPENCODE_PORT", "4096", 1) |
There was a problem hiding this comment.
Sync OPENCODE_PORT with forwarded opencode port
configureOMOEnvironment unconditionally sets OPENCODE_PORT to 4096 when the parent environment doesn't already define it, but runOMO still forwards user-supplied port flags unchanged. In flows like cmux omo --port 1234, OpenCode will run on 1234 while oh-my-opencode sees OPENCODE_PORT=4096, so subagent attach requests target the wrong API port and pane orchestration fails. The env var should be derived from the effective forwarded port (or left unset when a non-default port is provided).
Useful? React with 👍 / 👎.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The proportional equalize was treating the top-level horizontal split
(main vs agent column) the same as vertical splits, setting the main
pane to 1/6 of the window with 5 agents.
For main-vertical layout, only equalize vertical splits (the agent
column), leaving the horizontal main/agent divider untouched. The
subsequent resize-pane -x handles the main pane width.
workspace.equalize_splits now accepts an optional orientation filter
("vertical" or "horizontal") to scope which splits get equalized.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
- Fix cmux omo --help: remove omo from the help-bypass guard so --help shows usage text instead of trying to launch opencode - Don't overwrite unreadable opencode.json: fail with an error instead of silently resetting to empty config - Drain installer pipes concurrently before waitUntilExit to prevent deadlock from full pipe buffers during bun/npm install
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Addressed actionable review comments in 5487d5e:
Skipped (non-actionable / already addressed):
|
After each teammate split-window, call workspace.equalize_splits with orientation: "vertical" to evenly distribute panes in the agent column without affecting the leader/column horizontal divider. Uses the equalize_splits socket method already added by the omo integration (PR #2087) rather than duplicating it.
After each teammate split-window, call workspace.equalize_splits with orientation: "vertical" and deferred: true. The deferred flag schedules the equalization on the next main-thread run loop iteration, after bonsplit has assigned geometry to the new pane. Without deferral, the equalization call fires before the layout engine has flushed, so newly created panes have zero size and the proportional calculation is wrong. Uses the equalize_splits socket method already added by the omo integration (PR #2087) rather than duplicating it.
After each teammate split-window, call workspace.equalize_splits with orientation: "vertical" to evenly distribute panes in the agent column without affecting the leader/column horizontal divider. Uses the equalize_splits socket method from the omo integration (PR #2087). The equalization works synchronously because bonsplit's setDividerPosition sets ratios on the internal split state directly, no layout flush needed.
…allback (#2207) * Fix tmux compat store decoding, layout cleanup, and cross-workspace fallback Three hardening fixes for the tmux compatibility layer: 1. Add custom init(from:) to TmuxCompatStore using decodeIfPresent so older store files missing mainVerticalLayouts/lastSplitSurface keys decode gracefully instead of silently resetting to an empty store. 2. Clear mainVerticalLayouts entry when a non-main-vertical layout is selected, preventing stale state from redirecting future splits. 3. Only enter caller-anchoring block in split-window when resolveWorkspaceId succeeds, avoiding cross-workspace splits when the fallback target.workspaceId differs from the caller's workspace. * Add auto-equalize after teammate splits After each teammate split-window, call workspace.equalize_splits with orientation: "vertical" to evenly distribute panes in the agent column without affecting the leader/column horizontal divider. Uses the equalize_splits socket method from the omo integration (PR #2087). The equalization works synchronously because bonsplit's setDividerPosition sets ratios on the internal split state directly, no layout flush needed. * Refactor: DRY up claude-teams and omo shared launcher code Extract shared functions from the nearly-identical claude-teams and omo integration code: - configureTmuxCompatEnvironment: parameterized env setup (tmux path prefix, bin env var, term override, extra vars) - createTmuxCompatShimDirectory: shared tmux shim creation with writeShimIfChanged - resolveExecutableInSearchPath: generic PATH search with optional skip predicate - Rename ClaudeTeamsFocusedContext -> TmuxCompatFocusedContext, claudeTeamsFocusedContext -> tmuxCompatFocusedContext, claudeTeamsResolvedSocketPath -> tmuxCompatResolvedSocketPath Both integrations are now thin wrappers over the shared functions. No behavior changes. * Address PR review comments: store load, stale state, pane targets 1. Move loadTmuxCompatStore() inside the caller-anchoring if-let so it's only called when env vars are present (avoids unnecessary file I/O on non-agent splits). 2. Clear lastSplitSurface alongside mainVerticalLayouts when switching away from main-vertical layout, preventing stale seed values on re-entry. 3. Resolve select-layout -t via tmuxResolvePaneTarget first (tmux accepts pane targets like %1), falling back to workspace target. Removes duplicate workspace resolution in the else branch. * Fix select-layout -t fallback: don't apply to wrong workspace When an explicit -t target fails to resolve, error instead of silently falling back to the caller's current workspace. Only use the current workspace as default when no -t was provided. --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
) * Add `cmux omo` command for OpenCode + oh-my-openagent integration Same pattern as `cmux claude-teams`: creates a tmux shim so oh-my-openagent's TmuxSessionManager spawns agents as native cmux splits instead of tmux panes. Sets TMUX/TMUX_PANE env vars, prepends shim to PATH, and execs into opencode. Closes manaflow-ai#2085 * Auto-install oh-my-opencode plugin when running cmux omo Before launching opencode, cmux omo now: - Checks if oh-my-opencode is registered in ~/.config/opencode/opencode.json - If not, creates/updates the config with the plugin entry - Checks if the npm package is installed in node_modules - If not, runs bun add (or npm install) to install it - Then proceeds with tmux shim setup and exec * Use shadow config dir to avoid modifying user's opencode setup Instead of writing directly to ~/.config/opencode/opencode.json, cmux omo now creates a shadow config at ~/.cmuxterm/omo-config/ that layers oh-my-opencode on top of the user's existing config. Symlinks node_modules, package.json, bun.lock, and plugin config from the original dir. Sets OPENCODE_CONFIG_DIR to the shadow directory. Running plain `opencode` remains unaffected. * Add Agent Integrations docs section with Claude Code Teams and oh-my-opencode pages Adds sectioned sidebar navigation to the docs site. The new Agent Integrations section contains separate pages for cmux claude-teams and cmux omo, documenting usage, tmux shim mechanics, directory layout, environment variables, and the shadow config approach. Both pages include a nightly-only warning. Full English and Japanese translations, nav item keys added to all 19 locales. * Remove uppercase from sidebar section headers * Add more spacing above and below sidebar section headers * Enable tmux mode in oh-my-opencode config, improve docs - cmux omo now writes tmux.enabled=true to the shadow oh-my-opencode.json config. Without this, oh-my-openagent's TmuxSessionManager won't spawn visual panes even though $TMUX is set (the config defaults to false). - Nightly warnings now link to /nightly instead of generic text. - Added "What you get" section to oh-my-opencode docs explaining the visual pane behavior (auto-layout, idle cleanup, queueing). - Added tmux.enabled step to first-run and how-it-works sections. * Add terminal-notifier shim to route oh-my-openagent notifications to cmux oh-my-openagent sends macOS notifications via terminal-notifier (args: -title <t> -message <m> [-activate <id>]). The shim in ~/.cmuxterm/omo-bin/terminal-notifier intercepts these calls and routes them through cmux notify, so notifications appear in cmux's sidebar panel instead of as raw macOS notifications. * Add pane geometry to tmux-compat for oh-my-openagent grid planning oh-my-openagent's TmuxSessionManager needs pane geometry (columns, rows, position, window dimensions) to decide where to spawn agent panes. Without this data, agents run headlessly. Server side: - pane.list v2 response now includes pixel_frame, cell_size, columns, rows per pane, plus container_frame at the top level - Uses BonsplitController.layoutSnapshot() for pixel geometry and ghostty_surface_size() for terminal grid dimensions CLI side: - tmuxEnrichContextWithGeometry() computes character-cell positions from pixel frames and cell dimensions for tmux format variables (pane_width, pane_height, pane_left, pane_top, pane_active, window_width, window_height) - list-panes now resolves pane targets (%uuid) via tmuxResolvePaneTarget instead of failing with "Workspace not found" - display-message enriched with geometry for format strings like #{pane_width},#{window_width} - tmux -V now returns "tmux 3.4" (needed by oh-my-openagent's tmux-path-resolver verification) * Add socket tests for tmux-compat pane geometry 6 tests verifying the geometry enrichment works end-to-end: - pane.list returns pixel_frame, columns, rows, cell_size, container_frame - tmux -V returns version string - list-panes -F renders geometry format variables as integers - list-panes -t %<uuid> resolves pane targets - display -p renders pane_width and window_width - After split, two panes have different positions and halved widths All 6 pass on macmini (cmux-macmini). * Handle tmux -V in shim script directly (no socket needed) oh-my-openagent's tmux-path-resolver runs tmux -V to verify the binary works. The __tmux-compat handler requires a socket connection, which may not be established at verification time. Handle -V in the bash shim directly to avoid the socket dependency. * Lower default tmux pane min widths for cmux omo oh-my-openagent defaults: main_pane_min_width=120, agent_pane_min_width=40, requiring 161+ columns. Most terminal windows are narrower, causing decideSpawnActions to return canSpawn=false and defer agents forever. cmux omo now sets: main_pane_min_width=60, agent_pane_min_width=30, main_pane_size=50, requiring only 91 columns. Also moved tmux -V handling into the bash shim to avoid needing a socket connection for the version check. * Resolve merge conflicts with main (main-vertical layout, focus param) - Keep upstream main-vertical layout anchoring from manaflow-ai#2119 - Keep upstream focus param (v2Bool) instead of no_focus - Combine with our -d flag handling: -d sets focus=false - Include customCommands nav item from main * Implement select-layout equalize and resize-pane absolute width When oh-my-openagent spawns agent panes, it calls select-layout main-vertical after each split to redistribute panes evenly, then resize-pane -x <columns> to set the main pane width. Both were previously no-ops, causing cascading uneven splits. Server side: - Add workspace.equalize_splits v2 API that calls the existing TabManager.equalizeSplits (sets all dividers to 0.5) CLI side: - select-layout now calls workspace.equalize_splits before tracking main-vertical state - resize-pane -x <columns> without directional flags now computes the pixel delta from current to desired width and resizes accordingly * Fix equalize to use proportional divider positions The previous equalize set all dividers to 0.5, which in a right- recursive binary tree (from successive splits) gives 50/25/12.5/6.25% instead of equal sizes. New algorithm counts leaf panes on each side of each split and sets the divider to N_left / (N_left + N_right). For 5 panes in a chain: 1/5, 1/4, 1/3, 1/2, giving each pane exactly 20%. * Fix select-layout main-vertical to only equalize vertical splits The proportional equalize was treating the top-level horizontal split (main vs agent column) the same as vertical splits, setting the main pane to 1/6 of the window with 5 agents. For main-vertical layout, only equalize vertical splits (the agent column), leaving the horizontal main/agent divider untouched. The subsequent resize-pane -x handles the main pane width. workspace.equalize_splits now accepts an optional orientation filter ("vertical" or "horizontal") to scope which splits get equalized. * Re-equalize agent column after kill-pane * Address PR review comments - Fix cmux omo --help: remove omo from the help-bypass guard so --help shows usage text instead of trying to launch opencode - Don't overwrite unreadable opencode.json: fail with an error instead of silently resetting to empty config - Drain installer pipes concurrently before waitUntilExit to prevent deadlock from full pipe buffers during bun/npm install --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
…allback (manaflow-ai#2207) * Fix tmux compat store decoding, layout cleanup, and cross-workspace fallback Three hardening fixes for the tmux compatibility layer: 1. Add custom init(from:) to TmuxCompatStore using decodeIfPresent so older store files missing mainVerticalLayouts/lastSplitSurface keys decode gracefully instead of silently resetting to an empty store. 2. Clear mainVerticalLayouts entry when a non-main-vertical layout is selected, preventing stale state from redirecting future splits. 3. Only enter caller-anchoring block in split-window when resolveWorkspaceId succeeds, avoiding cross-workspace splits when the fallback target.workspaceId differs from the caller's workspace. * Add auto-equalize after teammate splits After each teammate split-window, call workspace.equalize_splits with orientation: "vertical" to evenly distribute panes in the agent column without affecting the leader/column horizontal divider. Uses the equalize_splits socket method from the omo integration (PR manaflow-ai#2087). The equalization works synchronously because bonsplit's setDividerPosition sets ratios on the internal split state directly, no layout flush needed. * Refactor: DRY up claude-teams and omo shared launcher code Extract shared functions from the nearly-identical claude-teams and omo integration code: - configureTmuxCompatEnvironment: parameterized env setup (tmux path prefix, bin env var, term override, extra vars) - createTmuxCompatShimDirectory: shared tmux shim creation with writeShimIfChanged - resolveExecutableInSearchPath: generic PATH search with optional skip predicate - Rename ClaudeTeamsFocusedContext -> TmuxCompatFocusedContext, claudeTeamsFocusedContext -> tmuxCompatFocusedContext, claudeTeamsResolvedSocketPath -> tmuxCompatResolvedSocketPath Both integrations are now thin wrappers over the shared functions. No behavior changes. * Address PR review comments: store load, stale state, pane targets 1. Move loadTmuxCompatStore() inside the caller-anchoring if-let so it's only called when env vars are present (avoids unnecessary file I/O on non-agent splits). 2. Clear lastSplitSurface alongside mainVerticalLayouts when switching away from main-vertical layout, preventing stale seed values on re-entry. 3. Resolve select-layout -t via tmuxResolvePaneTarget first (tmux accepts pane targets like %1), falling back to workspace target. Removes duplicate workspace resolution in the else branch. * Fix select-layout -t fallback: don't apply to wrong workspace When an explicit -t target fails to resolve, error instead of silently falling back to the caller's current workspace. Only use the current workspace as default when no -t was provided. --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Summary
cmux omo [opencode-args...]command that launches OpenCode with a tmux shim, same pattern ascmux claude-teamsTmuxSessionManagerspawns background agents in "tmux panes", they become native cmux splits with sidebar metadata and notifications~/.cmuxterm/omo-bin/tmuxshim redirecting tocmux __tmux-compat, setsTMUX/TMUX_PANEenv vars, prepends shim toPATHTesting
xcodebuild -scheme cmux -configuration Debugbuilds successfullyexecv)Related
cmux omocommand for oh-my-openagent / OpenCode integration #2085Summary by cubic
Adds
cmux omoto runopencodewithoh-my-openagent, turning agent “tmux panes” into native cmux splits with notifications and proportional layouts. Also adds Agent Integrations docs for this and Claude Code Teams (addresses #2085).New Features
cmux omo [opencode-args...]execs intoopencodevia a tmux-compat shim; forwards all args; injects--port 4096and setsOPENCODE_PORTunless provided.~/.cmuxterm/omo-bin/:tmux(handles-Vlocally; proxies others tocmux __tmux-compat) andterminal-notifier(routes tocmux notify); setsTMUX/TMUX_PANE/TERM; pointsOPENCODE_CONFIG_DIRto a shadow config.~/.cmuxterm/omo-configregisters and installsoh-my-opencodeviabun/npm, symlinksnode_modules/package.json/bun.lockand plugin config, enablestmux.enabled=true, and lowers defaults (main_pane_min_width=60,agent_pane_min_width=30,main_pane_size=50); plainopencodeis unaffected.list-panesresolves%panetargets;display-messagerenders geometry vars;split-window -dkeeps focus;select-layoutruns proportionalworkspace.equalize_splits(filters to vertical formain-vertical);resize-pane -x <cols>sets absolute width; re-equalizes afterkill-pane; supportstmux -V.oh-my-opencodeand Claude Code Teams; sidebar supports sections and pager navigation; localized strings updated.Bug Fixes
cmux omo --helpnow shows usage text instead of launchingopencode.opencode.jsonnow errors out instead of being overwritten.bun/npmstdout/stderr concurrently to avoid deadlocks.Written for commit 5487d5e. Summary will update on new commits.
Summary by CodeRabbit
omoCLI command to launch oh-my-opencode workflows with tmux-compatible invocation and standalone help.