Skip to content

Add cmux omo command for oh-my-openagent integration#2087

Merged
lawrencecchen merged 20 commits intomainfrom
issue-2085-cmux-omo
Mar 26, 2026
Merged

Add cmux omo command for oh-my-openagent integration#2087
lawrencecchen merged 20 commits intomainfrom
issue-2085-cmux-omo

Conversation

@lawrencecchen
Copy link
Copy Markdown
Contributor

@lawrencecchen lawrencecchen commented Mar 25, 2026

Summary

  • Adds cmux omo [opencode-args...] command that launches OpenCode with a tmux shim, same pattern as cmux claude-teams
  • When oh-my-openagent's TmuxSessionManager spawns background agents in "tmux panes", they become native cmux splits with sidebar metadata and notifications
  • Creates ~/.cmuxterm/omo-bin/tmux shim redirecting to cmux __tmux-compat, sets TMUX/TMUX_PANE env vars, prepends shim to PATH
  • Includes English and Japanese localization for usage text

Testing

  • xcodebuild -scheme cmux -configuration Debug builds successfully
  • No meaningful automated test is practical (CLI launches external process via execv)

Related


Summary by cubic

Adds cmux omo to run opencode with oh-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 into opencode via a tmux-compat shim; forwards all args; injects --port 4096 and sets OPENCODE_PORT unless provided.
    • Shims in ~/.cmuxterm/omo-bin/: tmux (handles -V locally; proxies others to cmux __tmux-compat) and terminal-notifier (routes to cmux notify); sets TMUX/TMUX_PANE/TERM; points OPENCODE_CONFIG_DIR to a shadow config.
    • Shadow config ~/.cmuxterm/omo-config registers and installs oh-my-opencode via bun/npm, symlinks node_modules/package.json/bun.lock and plugin config, enables tmux.enabled=true, and lowers defaults (main_pane_min_width=60, agent_pane_min_width=30, main_pane_size=50); plain opencode is unaffected.
    • Tmux-compat: pane/window geometry in format context; list-panes resolves %pane targets; display-message renders geometry vars; split-window -d keeps focus; select-layout runs proportional workspace.equalize_splits (filters to vertical for main-vertical); resize-pane -x <cols> sets absolute width; re-equalizes after kill-pane; supports tmux -V.
    • Docs: new “Agent Integrations” pages for oh-my-opencode and Claude Code Teams; sidebar supports sections and pager navigation; localized strings updated.
  • Bug Fixes

    • cmux omo --help now shows usage text instead of launching opencode.
    • Unreadable opencode.json now errors out instead of being overwritten.
    • Installer drains bun/npm stdout/stderr concurrently to avoid deadlocks.

Written for commit 5487d5e. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Added an omo CLI command to launch oh-my-opencode workflows with tmux-compatible invocation and standalone help.
  • Documentation
    • Added "Oh My OpenCode" and "Claude Code Teams" docs pages; docs sidebar and pager now support nested sections and proper prev/next navigation.
  • Localization
    • Added navigation labels and localized strings for the new docs across many languages, plus CLI usage text in English and Japanese.

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
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Mar 26, 2026 11:01pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new omo CLI subcommand with early-help handling and a run path that prepares a shadow OpenCode config, ensures/installs the oh-my-opencode plugin, creates a per-user tmux shim, sets tmux/cmux env vars and PATH, and execs into the opencode binary forwarding arguments.

Changes

Cohort / File(s) Summary
CLI omo Command Implementation
CLI/cmux.swift
Add omo subcommand, early --help dispatch, and runOMO flow: create shadow config (~/.cmuxterm/omo-config), merge oh-my-opencode into opencode.json, symlink node_modules/config, optionally install plugin via bun/npm, create ~/.cmuxterm/omo-bin/tmux shim, prepend shim to PATH, set TMUX/TMUX_PANE/TERM and CMUX env vars, resolve and exec opencode, and surface exec failures.
Localizable strings
Resources/Localizable.xcstrings
Add cli.omo.usage localized usage/help text (English and Japanese) describing cmux omo behavior and examples.
Docs navigation model & helpers
web/app/[locale]/components/docs-nav-items.ts
Introduce typed NavEntry model with NavLink/NavSection, add isSection and flatNavItems, and change navItems to support nested sections.
Docs pager navigation
web/app/[locale]/components/docs-pager.tsx
Use flatNavItems to compute prev/next neighbors from a flattened nav list instead of the nested navItems.
Docs sidebar rendering
web/app/[locale]/components/docs-sidebar.tsx
Add SidebarLink helper for centralized active styling; render navItems with section branching using isSection(entry) and render section children indented.
New docs pages
web/app/[locale]/docs/agent-integrations/oh-my-opencode/page.tsx, web/app/[locale]/docs/agent-integrations/claude-code-teams/page.tsx
Add two i18n-aware Next.js docs pages with generateMetadata and localized content (callouts, code blocks, tables, lists) describing agent integration shims and environment details.
Web localization messages
web/messages/*.json (multiple locales, e.g., en.json, ja.json, ar.json, ...)
Add navItems.agentIntegrations, navItems.claudeCodeTeams, navItems.ohMyOpenCode and related translation keys and content for the two new docs pages across many locales.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 beneath the keys I hop and hum,
I stitch a shim where tmux will come.
I link and add the tiny code,
then exec to opencode down the road.
Forwarded args — a rabbit's drum.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a new cmux omo command for oh-my-openagent integration, which is the primary feature of this PR.
Description check ✅ Passed The PR description covers what changed (cmux omo command, tmux shim, oh-my-opencode integration), why (native cmux splits with sidebar and notifications), and testing (successful build), but demo video and bot review checklist are incomplete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-2085-cmux-omo

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR adds cmux omo [opencode-args...] as a peer to the existing cmux claude-teams command. It sets up a ~/.cmuxterm/omo-bin/tmux shim, injects fake TMUX/TMUX_PANE environment variables, and execvs into opencode so that oh-my-openagent's TmuxSessionManager creates native cmux splits instead of real tmux panes.

  • The implementation is a clean, near-identical port of runClaudeTeams / configureClaudeTeamsEnvironment / createClaudeTeamsShimDirectory with opencode-specific naming — the structural parity makes the code easy to audit.
  • configureOMOEnvironment correctly uses prependPathEntries to insert the shim dir at the front of PATH and calls setenv / unsetenv before execv, which is the right approach for inheriting the modified environment into the child process.
  • resolveOpenCodeExecutable is called against the pre-shim PATH, so it cannot accidentally resolve the tmux shim as opencode.
  • The argv defer/free block mirrors runClaudeTeams exactly; free(nil) is a no-op, so the nil sentinel appended for execv is handled safely.
  • English and Japanese localization strings for cli.omo.usage are added; the entry is consistent with cli.claude-teams.usage in structure.
  • One minor consistency gap: resolveOpenCodeExecutable does not include a wrapper-detection guard comparable to the isCmuxClaudeWrapper check in resolveClaudeExecutable. This is not a present-day bug but breaks the structural symmetry with the claude-teams resolver.

Confidence Score: 5/5

  • Safe to merge — the implementation is a faithful port of the proven claude-teams pattern with no logic bugs.
  • The new omo command closely mirrors claude-teams in every material respect: shim creation, environment setup, PATH manipulation, execv launch, and localization. All the correctness properties that make claude-teams work apply equally here. The one gap (missing wrapper-detection guard in resolveOpenCodeExecutable) is a non-blocking style issue with no practical impact today.
  • No files require special attention.

Important Files Changed

Filename Overview
CLI/cmux.swift Adds runOMO, createOMOShimDirectory, configureOMOEnvironment, and resolveOpenCodeExecutable following the established claude-teams pattern. The implementation is correct; minor consistency gap: resolveOpenCodeExecutable lacks the wrapper-detection guard present in resolveClaudeExecutable.
Resources/Localizable.xcstrings Adds cli.omo.usage with English and Japanese translations, consistent with the existing cli.claude-teams.usage entry structure.

Sequence Diagram

sequenceDiagram
    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
Loading

Reviews (1): Last reviewed commit: "Add `cmux omo` command for OpenCode + oh..." | Re-trigger Greptile

Comment thread CLI/cmux.swift
Comment on lines +9418 to +9427
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Suggested change
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)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift
Comment on lines +9438 to +9441
#!/usr/bin/env bash
set -euo pipefail
exec "${CMUX_OMO_CMUX_BIN:-cmux}" __tmux-compat "$@"
"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
CLI/cmux.swift (1)

9416-9540: Extract the tmux-shim launcher instead of copying claude-teams.

CMUXCLI.createOMOShimDirectory(), CMUXCLI.configureOMOEnvironment(processEnvironment:shimDirectory:executablePath:socketPath:explicitPassword:focusedContext:), and CMUXCLI.runOMO(commandArgs:socketPath:explicitPassword:) are now almost a line-for-line copy of the claude-teams path. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7ffa447 and 697f4ba.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • Resources/Localizable.xcstrings

Comment thread CLI/cmux.swift
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

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
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift Outdated
Comment on lines +9477 to +9481
if let data = try? Data(contentsOf: jsonURL),
let existing = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
config = existing
} else {
config = [:]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread CLI/cmux.swift Outdated
Comment on lines +9524 to +9528
process.standardError = stderr

FileHandle.standardError.write("Installing oh-my-opencode plugin...\n".data(using: .utf8)!)
try process.run()
process.waitUntilExit()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
CLI/cmux.swift (1)

1414-1417: ⚠️ Potential issue | 🟠 Major

cmux omo --help still falls into the side-effecting launcher.

Line 1416 still excludes omo from the early help path, so cmux omo --help reaches runOMO() instead of printing the cli.omo.usage text. With the new bootstrap flow, that now also mutates ~/.config/opencode and may run bun add / npm install just 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ffb34218-7937-4220-9d88-97d5544a573c

📥 Commits

Reviewing files that changed from the base of the PR and between 697f4ba and 60aca2a.

📒 Files selected for processing (1)
  • CLI/cmux.swift

Comment thread CLI/cmux.swift Outdated
Comment on lines +9461 to +9468
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)@") }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CLI/cmux.swift Outdated
Comment thread CLI/cmux.swift
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.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift
Comment on lines +9519 to +9523
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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift
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),
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

…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.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift
Comment on lines +9552 to +9554
// 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 60aca2a and 763d7b1.

📒 Files selected for processing (25)
  • CLI/cmux.swift
  • web/app/[locale]/components/docs-nav-items.ts
  • web/app/[locale]/components/docs-pager.tsx
  • web/app/[locale]/components/docs-sidebar.tsx
  • web/app/[locale]/docs/agent-integrations/claude-code-teams/page.tsx
  • web/app/[locale]/docs/agent-integrations/oh-my-opencode/page.tsx
  • web/messages/ar.json
  • web/messages/bs.json
  • web/messages/da.json
  • web/messages/de.json
  • web/messages/en.json
  • web/messages/es.json
  • web/messages/fr.json
  • web/messages/it.json
  • web/messages/ja.json
  • web/messages/km.json
  • web/messages/ko.json
  • web/messages/no.json
  • web/messages/pl.json
  • web/messages/pt-BR.json
  • web/messages/ru.json
  • web/messages/th.json
  • web/messages/tr.json
  • web/messages/zh-CN.json
  • web/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

Comment on lines +11 to +13
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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment thread web/messages/de.json
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread web/messages/ar.json
"apiReference": "مرجع الواجهة البرمجية",
"browserAutomation": "أتمتة المتصفح",
"notifications": "الإشعارات",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "تكاملات الوكلاء",
Fix with Cubic

Comment thread web/messages/it.json
Comment on lines +554 to +556
"agentIntegrations": "Agent Integrations",
"claudeCodeTeams": "Claude Code Teams",
"ohMyOpenCode": "oh-my-opencode",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"claudeCodeTeams": "Claude Code Teams",
"ohMyOpenCode": "oh-my-opencode",
"agentIntegrations": "Integrazioni agenti",
"claudeCodeTeams": "Team Claude Code",
"ohMyOpenCode": "oh-my-opencode",
Fix with Cubic

Comment thread web/messages/da.json
"apiReference": "API-reference",
"browserAutomation": "Browserautomatisering",
"notifications": "Notifikationer",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Agentintegrationer",
Fix with Cubic

Comment thread web/messages/tr.json
"apiReference": "API Referansı",
"browserAutomation": "Tarayıcı Otomasyonu",
"notifications": "Bildirimler",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Ajan Entegrasyonları",
Fix with Cubic

Comment thread web/messages/es.json
"apiReference": "Referencia de API",
"browserAutomation": "Automatización del navegador",
"notifications": "Notificaciones",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Integraciones de agentes",
Fix with Cubic

Comment thread web/messages/zh-TW.json
"apiReference": "API 參考",
"browserAutomation": "瀏覽器自動化",
"notifications": "通知",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Agent 整合",
Fix with Cubic

Comment thread web/messages/de.json
"apiReference": "API-Referenz",
"browserAutomation": "Browser-Automatisierung",
"notifications": "Benachrichtigungen",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Agent-Integrationen",
Fix with Cubic

Comment thread web/messages/no.json
"apiReference": "API-referanse",
"browserAutomation": "Nettleserautomatisering",
"notifications": "Varsler",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "Agentintegrasjoner",
Fix with Cubic

Comment thread web/messages/zh-CN.json
"apiReference": "API 参考",
"browserAutomation": "浏览器自动化",
"notifications": "通知",
"agentIntegrations": "Agent Integrations",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"agentIntegrations": "智能体集成",
Fix with Cubic

Comment thread web/messages/pl.json
Comment on lines +554 to +555
"agentIntegrations": "Agent Integrations",
"claudeCodeTeams": "Claude Code Teams",
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
"agentIntegrations": "Agent Integrations",
"claudeCodeTeams": "Claude Code Teams",
"agentIntegrations": "Integracje agentów",
"claudeCodeTeams": "Zespoły Claude Code",
Fix with Cubic

- 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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CLI/cmux.swift
// 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")
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

- 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
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CLI/cmux.swift
}
unsetenv("TERM_PROGRAM")
// Tell oh-my-opencode the API server port so subagent attach works
if processEnvironment["OPENCODE_PORT"] == nil {
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Fix with Cubic

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift
Comment on lines 10033 to +10037
let created = try client.sendV2(method: "surface.split", params: [
"workspace_id": target.workspaceId,
"surface_id": target.surfaceId,
"direction": direction,
"focus": false
"focus": focusNewPane
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread CLI/cmux.swift
Comment on lines +9892 to +9894
if !commandArgs.contains("--port") {
effectiveArgs.append("--port")
effectiveArgs.append("4096")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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%.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread CLI/cmux.swift
Comment on lines +9844 to +9845
if processEnvironment["OPENCODE_PORT"] == nil {
setenv("OPENCODE_PORT", "4096", 1)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

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.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

- 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
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@lawrencecchen
Copy link
Copy Markdown
Contributor Author

Addressed actionable review comments in 5487d5e:

  1. cmux omo --help bypass (CodeRabbit): Removed omo from the help-bypass guard so --help shows usage text.
  2. Don't overwrite unreadable opencode.json (CodeRabbit/Codex/Cubic): Now fails with an error instead of silently resetting.
  3. Drain installer pipes (Codex/Cubic): Drains stdout/stderr concurrently via DispatchGroup before waitUntilExit to prevent deadlock.

Skipped (non-actionable / already addressed):

  • Pane metric fields (Codex P1): Already implemented in later commits (pane geometry, format variables).
  • Wrapper detection guard (Greptile P2): Future-proofing, no cmux-opencode wrapper exists today.
  • Version-pinned plugin entries (CodeRabbit): Edge case, bun add already respects pinned versions in package.json.
  • Extract shared tmux-shim launcher (Codex nitpick): Valid refactor suggestion for follow-up, not blocking.

@lawrencecchen lawrencecchen merged commit 84af32c into main Mar 26, 2026
13 checks passed
@lawrencecchen lawrencecchen deleted the issue-2085-cmux-omo branch March 26, 2026 23:08
lawrencecchen added a commit that referenced this pull request Mar 26, 2026
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.
lawrencecchen added a commit that referenced this pull request Mar 27, 2026
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.
lawrencecchen added a commit that referenced this pull request Mar 27, 2026
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.
lawrencecchen added a commit that referenced this pull request Mar 27, 2026
…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>
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
)

* 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>
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant