* 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 https://github.com/manaflow-ai/cmux/issues/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 #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>
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import { useTranslations } from "next-intl";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { CodeBlock } from "../../../components/code-block";
|
|
import { Callout } from "../../../components/callout";
|
|
import { Link } from "../../../../../i18n/navigation";
|
|
|
|
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
|
|
const { locale } = await params;
|
|
const t = await getTranslations({ locale, namespace: "docs.claudeCodeTeams" });
|
|
return {
|
|
title: t("metaTitle"),
|
|
description: t("metaDescription"),
|
|
};
|
|
}
|
|
|
|
export default function ClaudeCodeTeamsPage() {
|
|
const t = useTranslations("docs.claudeCodeTeams");
|
|
|
|
return (
|
|
<>
|
|
<h1>{t("title")}</h1>
|
|
|
|
<Callout type="warn">
|
|
{t.rich("nightlyWarning", {
|
|
nightly: (chunks) => <Link href="/nightly" className="underline">{chunks}</Link>,
|
|
})}
|
|
</Callout>
|
|
|
|
<p>{t("intro")}</p>
|
|
|
|
<h2>{t("usage")}</h2>
|
|
<CodeBlock lang="bash">{`cmux claude-teams
|
|
cmux claude-teams --continue
|
|
cmux claude-teams --model sonnet`}</CodeBlock>
|
|
<p>{t("usageDesc")}</p>
|
|
|
|
<h2>{t("howItWorks")}</h2>
|
|
<p>{t("howItWorksDesc")}</p>
|
|
<ul>
|
|
<li>{t("shimStep1")}</li>
|
|
<li>{t("shimStep2")}</li>
|
|
<li>{t("shimStep3")}</li>
|
|
<li>{t("shimStep4")}</li>
|
|
</ul>
|
|
|
|
<h2>{t("envVars")}</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>{t("envVarName")}</th>
|
|
<th>{t("envVarPurpose")}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><code>TMUX</code></td><td>{t("envTmux")}</td></tr>
|
|
<tr><td><code>TMUX_PANE</code></td><td>{t("envTmuxPane")}</td></tr>
|
|
<tr><td><code>CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS</code></td><td>{t("envTeams")}</td></tr>
|
|
<tr><td><code>CMUX_SOCKET_PATH</code></td><td>{t("envSocket")}</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>{t("directories")}</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>{t("dirPath")}</th>
|
|
<th>{t("dirPurpose")}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td><code>~/.cmuxterm/claude-teams-bin/</code></td><td>{t("dirShim")}</td></tr>
|
|
<tr><td><code>~/.cmuxterm/tmux-compat-store.json</code></td><td>{t("dirStore")}</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2>{t("tmuxCommands")}</h2>
|
|
<p>{t("tmuxCommandsDesc")}</p>
|
|
<ul>
|
|
<li><code>new-session</code>, <code>new-window</code> → {t("mapWorkspace")}</li>
|
|
<li><code>split-window</code> → {t("mapSplit")}</li>
|
|
<li><code>send-keys</code> → {t("mapSendText")}</li>
|
|
<li><code>capture-pane</code> → {t("mapReadText")}</li>
|
|
<li><code>select-pane</code>, <code>select-window</code> → {t("mapFocus")}</li>
|
|
<li><code>kill-pane</code>, <code>kill-window</code> → {t("mapClose")}</li>
|
|
<li><code>list-panes</code>, <code>list-windows</code> → {t("mapList")}</li>
|
|
</ul>
|
|
</>
|
|
);
|
|
}
|