remobi-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseremobi-setup
remobi-setup
Interactive onboarding skill for remobi — monitor and control tmux from your phone.
This skill walks the user through the full setup journey in one conversation. Each phase builds on the last; skip phases the user doesn't need.
为remobi打造的交互式入门技能——可通过手机监控和控制tmux。
该技能会在一次对话中引导用户完成完整的设置流程,每个阶段都基于上一阶段推进;用户可跳过不需要的阶段。
Workflow
工作流程
Phase 1: Assess environment
阶段1:环境评估
Check what's installed and help fill gaps.
bash
node --version # need >= 22
which ttyd # must be on PATH
tmux -V # target multiplexer
which remobi # npm install -g remobiIf anything is missing, help install it:
- Node: suggest mise, nvm, or direct install
- ttyd: (macOS), distro package or source build (Linux) — see ttyd installation
brew install ttyd - tmux: or distro package
brew install tmux - remobi:
npm install -g remobi
Move on once all four are present.
检查已安装的组件并帮助补充缺失项。
bash
node --version # 需要 >= 22
which ttyd # 必须在PATH中
tmux -V # 目标多路复用器
which remobi # npm install -g remobi若有组件缺失,帮助用户安装:
- Node:推荐使用mise、nvm或直接安装
- ttyd:macOS使用;Linux使用发行版包或源码构建——详见ttyd安装文档
brew install ttyd - tmux:使用或发行版包
brew install tmux - remobi:
npm install -g remobi
所有四个组件都安装完成后再进入下一阶段。
Phase 2: Inspect tmux setup
阶段2:检查tmux设置
Gather the user's tmux configuration to inform config generation.
bash
tmux show-options -g prefix # prefix key
tmux list-keys # all bindings
tmux show-options -g mouse # mouse mode
tmux show-options -g status-left # status bar
tmux list-keys | grep display-popup # popup bindingsIf tmux isn't running, fall back to reading the config file directly:
bash
cat ~/.config/tmux/tmux.conf 2>/dev/null || cat ~/.tmux.conf 2>/dev/nullNote down:
- Prefix key and byte (Ctrl-B = , Ctrl-A =
\x02, etc.)\x01 - Custom bindings worth surfacing as buttons (especially popup bindings for lazygit, yazi, neovim, fzf pickers, gh-dash, scratch shells)
- Whether mouse mode is on
- Status bar complexity (affects mobile width recommendations)
- Plugin manager (tpm, etc.)
If the user has no tmux config at all, offer to help set up a basic one before continuing.
收集用户的tmux配置信息,为生成remobi配置提供依据。
bash
tmux show-options -g prefix # 前缀键
tmux list-keys # 所有绑定
tmux show-options -g mouse # 鼠标模式
tmux show-options -g status-left # 状态栏
tmux list-keys | grep display-popup # 弹窗绑定若tmux未运行,则直接读取配置文件:
bash
cat ~/.config/tmux/tmux.conf 2>/dev/null || cat ~/.tmux.conf 2>/dev/null记录以下信息:
- 前缀键及其字节码(Ctrl-B = ,Ctrl-A =
\x02等)\x01 - 值得设置为手机端按钮的自定义绑定(尤其是针对lazygit、yazi、neovim、fzf选择器、gh-dash、临时shell的弹窗绑定)
- 鼠标模式是否开启
- 状态栏复杂度(影响移动端宽度建议)
- 插件管理器(如tpm)
若用户完全没有tmux配置,可先帮助其设置基础配置,再继续后续流程。
Phase 3: Interview the user
阶段3:用户访谈
Ask questions one at a time — don't dump a list. Adapt based on what you learned in phase 2.
- What do you primarily use tmux for? (coding agents, dev workflow, server monitoring, all of the above)
- Do you use popup bindings for tools? Which ones? (lazygit, yazi, neovim, scratch shell, gh-dash, session picker)
- Do you want touch scrolling? What strategy? (for mouse-event scrolling,
wheelfor PageUp/PageDown paging)keys - Auto-zoom on mobile? When you open remobi on your phone, should the current pane zoom to full screen automatically?
- Floating zoom button? A persistent button overlaid on the terminal for one-tap zoom toggle
- Custom theme or Catppuccin Mocha? (Catppuccin Mocha is the default and looks great — only ask if the user's tmux theme is clearly different)
- Font preference? (default: JetBrainsMono NFM)
- Any other tmux bindings you want on your phone? (This catches anything the inspection missed)
Skip questions where you already know the answer from phase 2. Summarise what you've gathered before moving to config generation.
逐个提问——不要一次性列出所有问题。根据阶段2获取的信息调整问题。
- 你主要用tmux做什么?(编码代理、开发工作流、服务器监控,或以上所有)
- **你是否为工具设置了弹窗绑定?**具体是哪些?(lazygit、yazi、neovim、临时shell、gh-dash、会话选择器)
- **你需要触摸滚动吗?**采用哪种策略?(表示鼠标事件滚动,
wheel表示PageUp/PageDown翻页)keys - **移动端自动缩放?**在手机上打开remobi时,当前窗格是否应自动全屏缩放?
- **悬浮缩放按钮?**在终端上添加一个持久显示的按钮,一键切换缩放状态
- 自定义主题还是Catppuccin Mocha?(Catppuccin Mocha是默认主题,视觉效果出色——仅当用户的tmux主题明显不同时才询问)
- 字体偏好?(默认:JetBrainsMono NFM)
- 还有其他想要在手机端使用的tmux绑定吗?(这可以捕捉到阶段2检查中遗漏的内容)
若从阶段2已能得到答案,可跳过对应问题。在进入配置生成阶段前,总结已收集到的信息。
Phase 4: Generate remobi.config.ts
remobi.config.ts阶段4:生成remobi.config.ts
remobi.config.tsWrite the config using . Only include keys that differ from defaults — omit everything else.
defineConfig()typescript
import { defineConfig } from 'remobi'
export default defineConfig({
// Only non-default overrides here
})After writing, validate:
bash
remobi build --dry-runA zero exit with "Dry run: build" output means valid. Fix any errors and re-validate until clean.
See Config reference below for the full schema, allowed keys, action types, and escape codes.
使用编写配置文件。仅包含与默认值不同的配置项——其余项可省略。
defineConfig()typescript
import { defineConfig } from 'remobi'
export default defineConfig({
// 仅在此处填写非默认的配置项
})编写完成后进行验证:
bash
remobi build --dry-run若退出码为0且输出“Dry run: build”,则配置有效。修复所有错误后重新验证,直至验证通过。
完整的配置 schema、允许的配置键、操作类型及转义码,请查看下方的配置参考。
Phase 5: Suggest tmux mobile optimisations
阶段5:提供tmux移动端优化建议
Ask: "Would you like suggestions for making your tmux config more mobile-friendly?"
If yes, run the checks below. If tmux isn't running, read the config file directly. For full context and examples, read and .
references/mobile-tmux.mdreferences/mobile-panes.md| Check | Command | Good sign | Suggestion if missing |
|---|---|---|---|
| Responsive status-left | | Contains | Add width breakpoints to strip content on narrow terminals |
| Responsive status-right | | Contains | Progressive content stripping |
| Popup sizing | | Uses | Replace fixed char sizes with |
| Zoom indicator | | Contains | Add |
| Mouse mode | | | |
| Window renumbering | | | |
| Zoom-aware navigation | | Present | Add zoom-aware |
For each missing item, offer a concrete snippet the user can paste into . Suggest snippets only — never modify without explicit permission.
tmux.conftmux.conf询问用户:“是否需要让tmux配置更适配移动端的建议?”
若用户同意,执行以下检查。若tmux未运行,则直接读取配置文件。完整的背景信息和示例,请查看和。
references/mobile-tmux.mdreferences/mobile-panes.md| 检查项 | 命令 | 合格标准 | 缺失时的建议 |
|---|---|---|---|
| 响应式左侧状态栏 | | 包含 | 添加宽度断点,在窄终端上精简内容 |
| 响应式右侧状态栏 | | 包含 | 逐步精简内容 |
| 弹窗尺寸 | | 使用 | 将固定字符尺寸替换为 |
| 缩放指示器 | | 包含 | 添加 |
| 鼠标模式 | | | |
| 窗口重新编号 | | | |
| 缩放感知导航 | | 已配置 | 添加支持缩放感知的 |
对于每一项缺失的配置,提供可直接粘贴到的代码片段。仅提供代码片段——未经明确许可,切勿修改用户的。
tmux.conftmux.confPhase 6: Deployment guidance
阶段6:部署指导
Ask: "How do you want to access remobi from your phone?"
remobi is a remote-control surface for your terminal — never expose it to the public internet. All deployment options below keep access private.
Common options:
- Tailscale Serve (recommended) — HTTPS over your private tailnet. Read for the full guide.
references/tailscale-serve.md - Cloudflare Tunnel + Access — private tunnel with Cloudflare Access policies controlling who can connect (e.g. restrict by email, IdP group, device posture). Do not use unauthenticated quick tunnels.
- Local network only — on localhost behind your own VPN or private network.
remobi serve
For macOS users, mention and point to for persistent options.
--no-sleepreferences/keep-awake.mdFor users who want manual ttyd control, point to .
references/ttyd-flags.md询问用户:“你希望如何从手机访问remobi?”
remobi是终端的远程控制界面——切勿将其暴露在公网中。以下所有部署选项均保证访问的私密性。
常见选项:
- Tailscale Serve(推荐)——通过你的私有tailnet实现HTTPS访问。完整指南请查看。
references/tailscale-serve.md - Cloudflare Tunnel + Access——私有隧道,通过Cloudflare Access策略控制访问权限(例如按邮箱、身份提供商组、设备状态限制)。请勿使用未认证的快速隧道。
- 仅本地网络访问——在本地主机上运行,并通过自己的VPN或私有网络访问。
remobi serve
针对macOS用户,提及选项,并指向查看持久运行的方案。
--no-sleepreferences/keep-awake.md对于希望手动控制ttyd的用户,指向。
references/ttyd-flags.mdPhase 7: Summarise
阶段7:总结
Tell the user:
- What was configured and why (prefix byte, custom bindings, gestures, theme)
- How to start:
remobi serve - How to access from their phone (URL from deployment choice)
- PWA install: on mobile, tap "Add to Home Screen" for a standalone app experience
告知用户:
- 已配置的内容及原因(前缀字节码、自定义绑定、手势、主题)
- 启动方法:
remobi serve - 手机端访问方式(根据部署选择提供对应URL)
- PWA安装:在移动端点击“添加到主屏幕”,获得独立应用体验
Config reference
配置参考
Allowed root keys
允许的根配置键
Exactly these — validation rejects anything else:
name theme font toolbar drawer gestures mobile floatingButtons pwa reconnect仅允许以下配置键——验证器会拒绝其他所有键:
name theme font toolbar drawer gestures mobile floatingButtons pwa reconnectButtonAction union
ButtonAction 联合类型
| Required fields | Notes |
|---|---|---|
| | Optional |
| (none) | Opens Ctrl+key combo UI |
| (none) | Paste from clipboard |
| (none) | Opens Ctrl/Alt + key modal |
| (none) | Opens/closes command drawer |
Non- actions must NOT have or — the validator rejects them.
senddatakeyLabel | 必填字段 | 说明 |
|---|---|---|
| | 可选 |
| 无 | 打开Ctrl+键组合界面 |
| 无 | 从剪贴板粘贴 |
| 无 | 打开Ctrl/Alt + 键选择模态框 |
| 无 | 打开/关闭命令抽屉 |
非类型的操作不得包含或——否则会被验证器拒绝。
senddatakeyLabelControlButton shape
ControlButton 结构
Every button in toolbar rows, drawer, and floatingButtons uses this schema:
typescript
{
id: string // unique within its array
label: string // text shown on the button
description: string // shown in help overlay — keep user-facing and clear
action: ButtonAction
}工具栏、抽屉和悬浮按钮中的所有按钮均使用以下结构:
typescript
{
id: string // 在所属数组中唯一
label: string // 按钮上显示的文字
description: string // 在帮助浮层中显示——需清晰易懂,面向用户
action: ButtonAction
}Button array forms (toolbar.row1
, toolbar.row2
, drawer.buttons
)
toolbar.row1toolbar.row2drawer.buttons按钮数组形式(toolbar.row1
, toolbar.row2
, drawer.buttons
)
toolbar.row1toolbar.row2drawer.buttonsTwo forms — pick the least invasive:
typescript
// 1. Replace entirely (plain array)
toolbar: { row1: [{ id, label, description, action }, ...] }
// 2. Transform (function receives defaults, returns new array)
toolbar: { row2: (defaults) => defaults.filter(b => b.id !== 'q') }
// Function form covers all operations via standard JS:
// - Append: (d) => [...d, newBtn]
// - Prepend: (d) => [newBtn, ...d]
// - Remove: (d) => d.filter(b => b.id !== 'q')
// - Replace: (d) => d.map(b => b.id === 'tmux-prefix' ? newBtn : b)
// - Insert: (d) => { const i = d.findIndex(b => b.id === 'tab'); return [...d.slice(0,i), newBtn, ...d.slice(i)] }两种形式——选择侵入性最低的一种:
typescript
// 1. 完全替换(普通数组)
toolbar: { row1: [{ id, label, description, action }, ...] }
// 2. 转换(函数接收默认值,返回新数组)
toolbar: { row2: (defaults) => defaults.filter(b => b.id !== 'q') }
// 函数形式可通过标准JS实现所有操作:
// - 追加: (d) => [...d, newBtn]
// - 前置: (d) => [newBtn, ...d]
// - 删除: (d) => d.filter(b => b.id !== 'q')
// - 替换: (d) => d.map(b => b.id === 'tmux-prefix' ? newBtn : b)
// - 插入: (d) => { const i = d.findIndex(b => b.id === 'tab'); return [...d.slice(0,i), newBtn, ...d.slice(i)] }Floating buttons
悬浮按钮
Must use the grouped shape — a flat is rejected:
ControlButton[]typescript
floatingButtons: [
{
position: 'top-left', // required
direction: 'row', // optional: 'row' | 'column' (default 'row')
buttons: [{ id, label, description, action }],
},
]Valid positions:
top-left | top-right | top-centre | bottom-left | bottom-right | bottom-centre | centre-left | centre-right必须使用分组结构——扁平的会被拒绝:
ControlButton[]typescript
floatingButtons: [
{
position: 'top-left', // 必填
direction: 'row', // 可选: 'row' | 'column'(默认'row')
buttons: [{ id, label, description, action }],
},
]有效的位置值:
top-left | top-right | top-centre | bottom-left | bottom-right | bottom-centre | centre-left | centre-rightEscape-code cheat sheet
转义码速查表
Use these in and gesture / fields:
action.dataleftright| Key | Escape sequence | Notes |
|---|---|---|
| Ctrl-B (prefix) | | Default tmux prefix |
| Ctrl-A (prefix) | | screen/byobu/custom prefix |
| Ctrl-C | | Interrupt |
| Ctrl-D | | EOF / exit shell |
| Escape | | |
| Tab | | |
| Shift+Tab | | |
| Enter | | |
| Alt+Enter | | |
| Backspace | | DEL character |
| Up arrow | | |
| Down arrow | | |
| Right arrow | | |
| Left arrow | | |
| Page Up | | |
| Page Down | | |
| Space | | literal space |
可在和手势的/字段中使用以下转义码:
action.dataleftright| 按键 | 转义序列 | 说明 |
|---|---|---|
| Ctrl-B(前缀) | | tmux默认前缀 |
| Ctrl-A(前缀) | | screen/byobu/自定义前缀 |
| Ctrl-C | | 中断 |
| Ctrl-D | | EOF / 退出shell |
| Escape | | |
| Tab | | |
| Shift+Tab | | |
| Enter | | |
| Alt+Enter | | |
| Backspace | | DEL字符 |
| 上箭头 | | |
| 下箭头 | | |
| 右箭头 | | |
| 左箭头 | | |
| Page Up | | |
| Page Down | | |
| 空格 | | 字面空格 |
Composing tmux key sequences
组合tmux按键序列
tmux bindings are + . Concatenate the bytes:
prefixkeyCtrl-B + c → '\x02c' (new window)
Ctrl-B + n → '\x02n' (next window)
Ctrl-B + p → '\x02p' (previous window)
Ctrl-B + z → '\x02z' (zoom pane)
Ctrl-B + % → '\x02%' (split vertical — stock tmux)
Ctrl-B + " → '\x02"' (split horizontal — stock tmux)
Ctrl-B + [ → '\x02[' (copy mode)
Ctrl-B + d → '\x02d' (detach)For a custom prefix (e.g. Ctrl-A): replace with .
\x02\x01tmux绑定为 + 。将字节码拼接即可:
前缀按键Ctrl-B + c → '\x02c' (新建窗口)
Ctrl-B + n → '\x02n' (下一个窗口)
Ctrl-B + p → '\x02p' (上一个窗口)
Ctrl-B + z → '\x02z' (缩放窗格)
Ctrl-B + % → '\x02%' (垂直拆分——默认tmux)
Ctrl-B + " → '\x02"' (水平拆分——默认tmux)
Ctrl-B + [ → '\x02[' (复制模式)
Ctrl-B + d → '\x02d' (断开连接)对于自定义前缀(如Ctrl-A):将替换为即可。
\x02\x01Example configs
配置示例
Minimal — default Ctrl-B prefix, custom name only
极简配置——默认Ctrl-B前缀,仅自定义名称
typescript
import { defineConfig } from 'remobi'
export default defineConfig({
name: 'dev',
})typescript
import { defineConfig } from 'remobi'
export default defineConfig({
name: 'dev',
})Custom prefix — Ctrl-A (screen/byobu style)
自定义前缀——Ctrl-A(screen/byobu风格)
Replace the default button and update swipe gestures:
tmux-prefixtypescript
import { defineConfig } from 'remobi'
export default defineConfig({
name: 'dev',
toolbar: {
row1: (defaults) => defaults.map(b =>
b.id === 'tmux-prefix'
? { ...b, description: 'Send tmux prefix key (Ctrl-A)', action: { type: 'send', data: '\x01' } }
: b
),
},
gestures: {
swipe: {
left: '\x01n',
right: '\x01p',
leftLabel: 'Next tmux window',
rightLabel: 'Previous tmux window',
},
},
drawer: {
buttons: (defaults) => defaults.map(b => {
// Remap tmux-prefixed buttons from Ctrl-B (\x02) to Ctrl-A (\x01)
if (b.action.type === 'send' && b.action.data.startsWith('\x02')) {
return { ...b, action: { ...b.action, data: '\x01' + b.action.data.slice(1) } }
}
return b
}),
},
})替换默认的按钮并更新滑动手势:
tmux-prefixtypescript
import { defineConfig } from 'remobi'
export default defineConfig({
name: 'dev',
toolbar: {
row1: (defaults) => defaults.map(b =>
b.id === 'tmux-prefix'
? { ...b, description: '发送tmux前缀键(Ctrl-A)', action: { type: 'send', data: '\x01' } }
: b
),
},
gestures: {
swipe: {
left: '\x01n',
right: '\x01p',
leftLabel: '下一个tmux窗口',
rightLabel: '上一个tmux窗口',
},
},
drawer: {
buttons: (defaults) => defaults.map(b => {
// 将tmux前缀绑定从Ctrl-B(\x02)重新映射为Ctrl-A(\x01)
if (b.action.type === 'send' && b.action.data.startsWith('\x02')) {
return { ...b, action: { ...b.action, data: '\x01' + b.action.data.slice(1) } }
}
return b
}),
},
})Floating buttons + mobile auto-zoom
悬浮按钮 + 移动端自动缩放
typescript
import { defineConfig } from 'remobi'
export default defineConfig({
mobile: {
initData: '\x02z', // zoom focused pane on mobile load
widthThreshold: 768,
},
floatingButtons: [
{
position: 'top-left',
buttons: [
{
id: 'zoom',
label: 'Zoom',
description: 'Toggle pane zoom',
action: { type: 'send', data: '\x02z' },
},
],
},
],
})typescript
import { defineConfig } from 'remobi'
export default defineConfig({
mobile: {
initData: '\x02z', // 移动端加载时缩放当前聚焦的窗格
widthThreshold: 768,
},
floatingButtons: [
{
position: 'top-left',
buttons: [
{
id: 'zoom',
label: '缩放',
description: '切换窗格缩放状态',
action: { type: 'send', data: '\x02z' },
},
],
},
],
})Guardrails
注意事项
- Never invent root keys. The validator rejects unknown keys with a path-based error.
- Use , never
drawer.buttons— the latter was renamed and no longer works.drawer.commands - actions require
send— omitting it fails validation.data - Non-actions must not have
sendordata— validator rejects them.keyLabel - is an array of groups — wrap buttons in
floatingButtons.{ position, buttons } - has
toolbarandrow1— there is norow2or flatrow3key on toolbar.buttons - is
mobile.initData— set tostring | nullto disable, notnullorfalse.'' - has only
reconnect— defaults toenabled: boolean. Settrueto disable.{ enabled: false }
- 切勿自行添加根配置键。验证器会拒绝未知键,并返回基于路径的错误。
- 使用,切勿使用
drawer.buttons——后者已重命名,不再生效。drawer.commands - 类型操作必须包含
send——缺失会导致验证失败。data - 非类型操作不得包含
send或data——验证器会拒绝此类配置。keyLabel - 是分组数组——需将按钮包裹在
floatingButtons中。{ position, buttons } - 包含
toolbar和row1——不存在row2或直接在toolbar下的row3键。buttons - ****的类型为
mobile.initData——设置为string | null以禁用,切勿使用null或false。'' - ****仅包含
reconnect——默认值为enabled: boolean。设置true可禁用该功能。{ enabled: false }
Validation
验证
bash
remobi build --dry-run # validates config, prints plan, exits without building
remobi build --dry-run -c ./remobi.config.ts # explicit pathA zero exit with "Dry run: build" output means the config is valid. Any error output means fix the reported paths before proceeding.
bash
remobi build --dry-run # 验证配置,输出计划,不执行实际构建
remobi build --dry-run -c ./remobi.config.ts # 指定配置文件路径若退出码为0且输出“Dry run: build”,则配置有效。若有错误输出,需修复报告的路径问题后再继续。
Common validation errors
常见验证错误
| Error | Cause | Fix |
|---|---|---|
| Invented or legacy root key | Remove it; only allowed root keys are valid |
| Old key name | Rename to |
| Wrong toolbar shape | Use |
| Wrong type string | Use exact literal from ButtonAction union |
| | Add |
| | Remove |
| Flat | Wrap in group: |
| | Use |
| 错误信息 | 原因 | 修复方法 |
|---|---|---|
| 自行添加或使用了旧版根配置键 | 删除该键;仅使用允许的根配置键 |
| 使用了旧版键名 | 重命名为 |
| toolbar结构错误 | 使用 |
| 操作类型字符串错误 | 使用ButtonAction联合类型中的精确字面量 |
| | 添加 |
| 非 | 从非 |
| 使用了扁平的 | 将其包裹为分组: |
| 传入了 | 使用 |