Skip to content

feat: add --windows layout mode (separate tmux windows per provider)#145

Open
GuolongTang111 wants to merge 11 commits intobfly123:mainfrom
GuolongTang111:feat/windows-layout-mode
Open

feat: add --windows layout mode (separate tmux windows per provider)#145
GuolongTang111 wants to merge 11 commits intobfly123:mainfrom
GuolongTang111:feat/windows-layout-mode

Conversation

@GuolongTang111
Copy link
Copy Markdown

Summary / 概要

  • Add -w/--windows CLI flag and "layout": "windows" config option that creates each provider in its own tmux window instead of a split pane
    新增 -w/--windows 命令行参数及 "layout": "windows" 配置项,每个 provider 独占一个 tmux 窗口而非拆分面板
  • Each provider window gets a linked tmux session (e.g. 19-Claude, 19-Codex) so users can tmux attach -t <session>-<Provider> from any terminal for independent viewports
    每个 provider 窗口创建一个 linked tmux session(如 19-Claude19-Codex),可在任意终端独立连接
  • cmd (shell) pane stays in the anchor window -- no separate window for a utility shell
    cmd(shell)面板保留在锚点窗口中,不单独创建窗口
  • WezTerm guard: exits with clear error if --windows is used outside tmux
    WezTerm 防护:在非 tmux 环境下使用 --windows 时给出明确错误提示
  • Resume mismatch: detects stored vs current layout mode, launches fresh with warning instead of creating mixed state
    恢复时检测布局模式不匹配,给出提示并重新启动,避免混合状态
  • Layout placement extracted into PanesLayout/WindowsLayout strategy classes for clean separation
    布局逻辑抽取为 PanesLayout/WindowsLayout 策略类,职责清晰分离
  • 41 new unit tests; all 309 tests pass
    新增 41 个单元测试,全部 309 个测试通过

Changes / 改动

File / 文件 What / 说明
lib/layout.py PanesLayout / WindowsLayout 策略类
lib/terminal.py new_window(), focus_pane(), destroy_linked_session(), get_session_name(), rename_window(), create_linked_session() 方法;create_pane() 新增 layout_mode 参数
lib/pane_registry.py get_layout_mode() 访问器
lib/ccb_start_config.py "layout" 字段解析与校验
ccb --windows 参数、AILauncher 管线、策略驱动的 run_up()、WezTerm 防护、恢复不匹配检测、registry 写入 layout_mode、退出/kill 时清理 linked sessions
test/test_windows_layout.py 41 个测试(配置、注册表、后端方法、策略类)
test/test_ccb_tmux_split.py fake backend 签名更新(1 行)
README.md / README_zh.md 参数文档、配置示例、使用说明

Backwards compatible / 向后兼容

Default behavior unchanged -- layout defaults to "panes".
默认行为不变,layout 默认值为 "panes"

Test plan / 测试计划

  • 41 dedicated tests in test_windows_layout.py / 41 个专项测试
  • Full suite: 309 tests passing / 全量 309 测试通过
  • Manual: ccb start -w codex gemini creates separate tmux windows / 手动验证创建独立窗口
  • Manual: tmux attach -t <session>-Codex attaches to individual provider / 手动验证独立连接
  • Manual: ccb start codex gemini (without -w) still uses split panes / 手动验证默认仍为面板模式
  • Manual: switching layout mode on resume triggers fresh launch / 手动验证切换模式时重新启动

🤖 Generated with Claude Code

Guolong and others added 11 commits March 16, 2026 18:44
- TmuxBackend: add new_window() and focus_pane() methods
- TmuxBackend: extend create_pane() with layout_mode parameter
- pane_registry: add get_layout_mode() accessor
- ccb: add -w/--windows CLI flag and layout_mode to AILauncher
- ccb_start_config: parse "layout" field from config JSON

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- run_up(): add windows-mode branch that creates new tmux windows per
  provider instead of split panes, with window renaming
- WezTerm guard: exit with error if --windows used with non-tmux backend
- cmd pane: always splits inside anchor window, even in windows mode
- _start_provider/_start_*_tmux: thread layout_mode through to create_pane
- test: update fake backend to accept layout_mode parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Resume: detect stored vs current layout mode mismatch, skip pane
  reuse and launch fresh with warning when they differ
- Registry: store layout_mode in all 6 provider upsert_registry calls
- Optimize: reuse early config load to avoid duplicate file reads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Config parsing: layout field validation, defaults, case handling
- Registry: get_layout_mode accessor, persistence via upsert
- TmuxBackend: create_pane routing (new_window vs split_pane)
- new_window: session/name flags, failure handling
- focus_pane: window switching, error cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README.md: add flag table entry, config example, usage tips
- README_zh.md: matching Chinese translations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- new_window(): after creating window, also creates a linked session
  (tmux new-session -d -t {main} -s {main}-{provider}) and points it
  at the correct window for independent attach
- destroy_linked_session(): cleanup method for linked sessions
- AILauncher: track linked_sessions list, clean up on exit and kill
- Registry: store linked_sessions field for all providers
- Tests: 6 new tests for linked session create/destroy/failure

Users can now: tmux attach -t {session}-{provider} from any terminal
to view a provider independently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The anchor provider runs in-place and was skipped by the linked session
creation loop. Now the anchor window also gets its own linked session
so all providers are independently attachable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tmux session groups cause #{session_name} to return a linked session
name instead of the original after the first linked session is created.
This caused chained names like 19-Claude-Codex instead of 19-Codex.

Fix: query #{session_name} once before any linked sessions exist, then
reuse that value for all providers. Move linked session creation out of
new_window() into run_up() where the pre-captured name is available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove session_name/new-session/select-window stubs that were added for
linked session creation which has since been moved out of new_window().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove layout_mode parameter from 7 _start_* method signatures;
  read self.layout_mode directly (cmd pane hardcodes "panes")
- Extract _create_linked_session() helper to deduplicate the
  new-session + select-window + append logic in run_up()
- Extract _registry_base() helper to deduplicate the 6 identical
  registry dict constructions across _write_*_session methods

No logic changes -- pure structural cleanup. 292 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the orchestration logic from the big if/else in run_up() into
PanesLayout and WindowsLayout strategy classes. Extract get_session_name,
rename_window, and create_linked_session as proper TmuxBackend methods
instead of raw _tmux_run calls in the main script. Linked session
lifecycle is now encapsulated in WindowsLayout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@GuolongTang111 GuolongTang111 marked this pull request as draft March 16, 2026 20:50
@GuolongTang111 GuolongTang111 marked this pull request as ready for review March 16, 2026 20:51
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