feat: cmux as configurable backend for session management#1
Open
abhirup-dev wants to merge 3 commits intomainfrom
Open
feat: cmux as configurable backend for session management#1abhirup-dev wants to merge 3 commits intomainfrom
abhirup-dev wants to merge 3 commits intomainfrom
Conversation
Introduce a backend abstraction layer so agent-deck.nvim can use either
the existing agent-deck CLI daemon or cmux (native macOS terminal app)
for AI coding session management.
Key changes:
- backend.lua dispatch layer with init()/name() and method forwarding
- backend/cmux.lua: full implementation (launch, list, send, output, etc.)
using cmux CLI via the same vim.uv.spawn pattern as cli.lua
- backend/cmux_status.lua: .jsonl tail parser for Claude session status
detection (8KB seek, sub-millisecond)
- backend/ansi.lua: ANSI escape stripping for read-screen output
- backend/agent_deck.lua: thin cli.lua wrapper preserving current behavior
- claude_paths.lua: extracted shared path utilities (conv_exists, encode_path)
- session_cmd.lua: extracted shared build_cmd logic from picker/parallel
- persist.lua: _cmux_sessions accessors for cmux metadata storage
All existing callers (init, picker, parallel, send, output, info) rewired
from require("agent-deck.cli") to require("agent-deck.backend"). Backend
selection via setup({ backend = "cmux" }), defaults to "agent-deck".
Includes comprehensive logging and vim.notify calls on all critical paths
for debugging during testing phase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…coding - session_cmd: read ~/.agent-deck/config.toml for claude command/env, support custom_claude_cmd setup option, check session status to pick --resume vs --session-id correctly - claude_paths: encode both / and . to match Claude's actual project directory encoding (fixes .config paths producing wrong lookup) - init: codex thread poll sync — enrich codex sessions on every poll so Dar can resume the correct thread via stop→set→start (workaround for agent-deck restart ignoring command field) - parallel: use build_cmd_new with conv_exists check instead of build_cmd - picker: handle nil from build_cmd_new (waiting + no .jsonl), show notification after Dan if claude is waiting for permission - cmux: delegate launch command to session_cmd for config-aware builds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d sync Tested cmux backend end-to-end and fixed all issues found: CLI command mapping (cmux v0.63): - list-surfaces → tree --all --json (parse nested structure) - send-surface → send --surface, send-key-surface → send-key --surface - focus-surface → select-workspace (no focus-surface in cmux CLI) - ctrl-c → ctrl+c (plus sign) - Binary resolution: add /Applications/cmux.app/Contents/Resources/bin/cmux fallback Session lifecycle: - Launch: prepend cd <path> && to commands (surfaces default to ~) - Launch: pass --cwd to new-workspace for correct shell directory - Restart: close-surface + new-split (cmux has no process-kill API) - Restart: create new surface BEFORE closing old (avoids "last surface" error) - Restart: use bare tool name in persist, not resume command (prevents doubling) - Stop: same create-first-then-close pattern - Restart/start: build command from bare tool + session IDs, never from stored command (which may already contain resume args from a previous restart) Status detection (cmux_status.lua): - Skip system/attachment entries, find last assistant/human entry - Check message.stop_reason (not top-level stop_reason) Codex thread sync for cmux: - Separate poll block in init.lua (gated on backend name) - Reads from cmux persist, enriches via codex.enrich_session (shared util), writes codex_thread_id back to cmux persist - Store created_at as UTC date string (not unix int) so SQLite strftime works UI (picker + parallel): - Separate cmux enrichment path: enrich_cmux_sessions reads persist + build_cmd - _cmd_ready flag prevents double-processing through build_cmd_new - DaR for cmux: always fetch fresh sessions from backend (surface refs change after Dar restart), not gated on was_open - Daf: new cmux-only single-select picker for focusing session in cmux UI - Extracted do_open_split/do_open_float shared by both backends Backend separation: - Dar in init.lua: cmux path uses simple session_restart loop (no agent-deck codex stop→set→start workaround needed) - Agent-deck code paths completely untouched Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds cmux as a configurable backend alternative to the agent-deck CLI daemon. cmux is a native macOS terminal app purpose-built for AI agent orchestration, with GPU-accelerated rendering (libghostty), integrated browser, and rich sidebar metadata.
With this change, sessions can run in cmux's native UI instead of Neovim terminal buffers, while maintaining full feature parity with the existing backend.
Architecture
What changed
New files (7):
backend.lua— dispatch layer withinit(),name(), method forwarding for all 15 interface methodsbackend/agent_deck.lua— thincli.luawrapper +focus_sessionno-op (preserves existing behavior exactly)backend/cmux.lua— full cmux implementation: launch, list, send, output, start/stop/restart/delete, groups, focusbackend/cmux_status.lua— Claude.jsonltail parser for session status detection (sub-millisecond)backend/ansi.lua— ANSI escape sequence stripping for cleanread-screenoutputclaude_paths.lua— extracted shared path utilities frompicker.luasession_cmd.lua— extracted sharedbuild_cmdlogic previously duplicated betweenpicker.luaandparallel.luaModified files (7):
init.lua—backend.init(opts.backend)in setup; allclirefs swapped tobackend; sync timer gatedpersist.lua— added_cmux_sessionsaccessors (get/set/remove/all)ui/picker.lua— uses shared modules; addsfocus_sessionpath for cmuxui/parallel.lua— uses shared modules; cmux-aware layout delegates tofocus_sessionui/send.lua,ui/output.lua,ui/info.lua— mechanicalcli→backendswapFeature parity
agent-deck launchcmux new-workspace+new-split+send-surfaceagent-deck list --jsoncmux list-surfaces+ persist metadata + .jsonl statusagent-deck session sendcmux send-surface+send-key enteragent-deck session outputcmux read-screen+ ANSI stripsend-key ctrl-c/close-surfaceagent-deck groupcommandscmux focus-surface+select-workspaceConfiguration
Known limitations (cmux backend)
group_moveis persist-only (cmux doesn't support cross-workspace moves)read-screenoutput is ANSI-stripped raw text vs structured JSONReview fixes applied
session_deleteuse-after-delete — fixednew-workspacemissing--nameflag — fixedTest plan
:AgentDeckLog, statusline updatesDan,Dap,Dal,DaRall work (session lifecycle)Das+Dao(send/output)backend = "cmux": health check, launch, focus, send, output🤖 Generated with Claude Code