muxr 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: muxr-control
3
+ description: |
4
+ Use when driving a muxr terminal session — running commands across panes,
5
+ watching long-running processes, capturing terminal output, setting up
6
+ layouts, or working with the muxr drawer. Triggers when MUXR_SESSION is
7
+ set in the environment, or when the user asks to "run X in pane Y",
8
+ "what does pane N show", "switch the muxr layout", etc.
9
+ ---
10
+
11
+ # muxr-control
12
+
13
+ You're driving a [muxr](https://github.com/roelbondoc/muxr) terminal
14
+ multiplexer session through its MCP bridge. muxr is a tiling terminal
15
+ multiplexer (think tmux + xmonad). Each pane is a real shell PTY; you can
16
+ read its current screen contents, send keystrokes, and wait for output to
17
+ settle — without taking control of the user's keyboard.
18
+
19
+ ## First thing: ground yourself
20
+
21
+ Before doing anything else, call **`muxr_session_get`** and
22
+ **`muxr_panes_list`**. These are cheap, idempotent reads. They tell you:
23
+
24
+ - The session name, layout (tall / grid / monocle), and current dimensions.
25
+ - Each pane's stable id (6 hex chars, e.g. `a3f9b2`), its 1-based slot
26
+ number as shown on screen (`#1`, `#2`, …), its cwd, and whether it's the
27
+ focused or master pane.
28
+ - The `focused_pane` field in `session.get` tells you which pane the user
29
+ was last looking at — if the user just said "run X" without naming a
30
+ pane, that's the natural target.
31
+
32
+ ## Pane identity: **always use the id, never the slot**
33
+
34
+ The status bar shows panes as `#1 a3f9b2`, `#2 c2e810`, etc. The number is
35
+ a *slot* — purely positional and tied to where the pane sits in the array.
36
+ The hex string is the *id* — generated once at pane creation and stable
37
+ forever.
38
+
39
+ Slots shift when panes are created, killed, or promoted to master. The id
40
+ never moves. **Every tool call that names a pane should pass the id.** If
41
+ the user says "the second pane", look it up in `muxr_panes_list` and pass
42
+ the id you find at slot 2 — don't pass `2` directly even though it works,
43
+ because by the time the call lands the slots may have changed.
44
+
45
+ ## Recipes
46
+
47
+ ### Run a command and get its output
48
+
49
+ ```
50
+ muxr_pane_run({ "pane": "a3f9b2", "input": "ls -la" })
51
+ ```
52
+
53
+ This sends `ls -la\r` to pane `a3f9b2`, waits for the PTY to go idle (no
54
+ output for 500ms by default), and returns the pane's full visible text
55
+ plus a `timed_out` flag.
56
+
57
+ **Always prefer `muxr_pane_run` over `muxr_pane_send_input` + a separate
58
+ `muxr_pane_read`.** The split version races — the read can fire before
59
+ the shell has redrawn the prompt, and you'll miss the output entirely.
60
+
61
+ ### Tune `idle_ms` for the kind of command
62
+
63
+ - **Fast, simple commands** (`pwd`, `git status`): default 500ms is fine.
64
+ - **Bursty output** (test runners, builds): bump to `idle_ms: 800` or
65
+ `1000`. Test runners often pause briefly between phases; a too-short
66
+ idle window cuts off mid-run.
67
+ - **Interactive REPLs** that you want to type into without waiting for
68
+ completion: use `muxr_pane_send_input` directly with `append_enter:
69
+ false` — don't try to detect idleness on a REPL.
70
+ - **Long builds** (npm install, cargo build): bump `timeout_ms` to
71
+ `120000` or higher. Default is 30s.
72
+
73
+ ### Wait without sending anything
74
+
75
+ ```
76
+ muxr_pane_run({ "pane": "a3f9b2", "input": "", "append_enter": false,
77
+ "idle_ms": 1000, "timeout_ms": 30000 })
78
+ ```
79
+
80
+ Useful when the user has already typed a command and you want to capture
81
+ its output once it finishes.
82
+
83
+ ### Send multi-line input (paste mode)
84
+
85
+ ```
86
+ muxr_pane_send_input({
87
+ "pane": "a3f9b2",
88
+ "data": "def hello\n puts :world\nend\n",
89
+ "bracketed": true
90
+ })
91
+ ```
92
+
93
+ `bracketed: true` wraps the data in `\e[200~` / `\e[201~` so editors and
94
+ REPLs treat it as a single paste rather than N separate keystrokes (which
95
+ fires their auto-indent / autocomplete on every line).
96
+
97
+ ### Look at the drawer without opening it
98
+
99
+ ```
100
+ muxr_drawer_read({})
101
+ ```
102
+
103
+ The drawer's shell process keeps running while hidden — its scrollback
104
+ survives. You can read it any time without disturbing the user's view.
105
+
106
+ ### Set up a layout for a task
107
+
108
+ ```
109
+ muxr_layout_set({ "layout": "tall" })
110
+ muxr_pane_new({}) // create a second pane
111
+ muxr_pane_send_input({ "pane": "<new id>", "data": "npm run dev\n" })
112
+ ```
113
+
114
+ Avoid doing this unsolicited — the human owns the layout. Only restructure
115
+ when the user explicitly asks ("set up a dev environment", "split this
116
+ into 3 panes").
117
+
118
+ ## Gotchas
119
+
120
+ ### Reading is cheap. Writing is destructive.
121
+
122
+ `muxr_pane_read`, `muxr_panes_list`, `muxr_drawer_read`, and
123
+ `muxr_session_get` have zero side effects — call them whenever you need
124
+ to ground yourself. **Mutating tools** (`muxr_pane_send_input`,
125
+ `muxr_pane_run`, `muxr_pane_kill`, `muxr_layout_set`, …) affect the
126
+ user's live session. Before calling any of them:
127
+
128
+ - Confirm the user named the specific pane you're about to act on (or
129
+ agreed implicitly by saying "run X here").
130
+ - Double-check the id by reading `muxr_panes_list` if you haven't done so
131
+ recently.
132
+ - **Never `muxr_pane_kill`** without the user explicitly saying "close
133
+ pane X" — a pane often holds in-progress work that's not in any file.
134
+
135
+ ### `pane.read` returns *visible* text only
136
+
137
+ The result is the pane's current 80×24-or-whatever grid, with trailing
138
+ whitespace trimmed per row. Lines that have scrolled into scrollback are
139
+ not in the response. If you need older output, ask the user to scroll
140
+ the pane up first (they have `Ctrl-a [` for scrollback mode), or watch
141
+ the pane via `muxr_pane_run` while the command is running.
142
+
143
+ ### Private panes
144
+
145
+ The user can mark any pane *private* with `Ctrl-a P` (status bar shows
146
+ `[P]` after the pane id). Private panes appear in `muxr_panes_list` with
147
+ `"private": true` and *no* `cwd`/`rows`/`cols` — `muxr_pane_read`,
148
+ `muxr_pane_send_input`, `muxr_pane_run`, `muxr_pane_subscribe`, and
149
+ `muxr_pane_kill` all refuse with an error message that tells you the
150
+ human-side gesture to undo it.
151
+
152
+ When this happens: **do not retry**. Surface it to the user verbatim
153
+ ("pane #2 a3f9b2 is private; press Ctrl-a P on it to expose it to me").
154
+ The privacy flag is intentionally one-way from MCP's perspective: there
155
+ is no `muxr_pane_unmark_private` tool.
156
+
157
+ `muxr_pane_focus` and `muxr_pane_promote` still work on private panes
158
+ (they're layout ops, not content ops) — useful if the user asks to
159
+ "bring my private pane to the front" without exposing it.
160
+
161
+ ### The drawer might be Claude itself
162
+
163
+ If the bridge sees the env var `MUXR_DRAWER_SELF=1` it refuses
164
+ `muxr_drawer_*` methods — that means the bridge is running *inside* the
165
+ muxr drawer and the call would recurse into your own pty. If you get
166
+ that error, that's why: you can still drive the surrounding tiled panes
167
+ normally, you just can't toggle/read the drawer that's hosting you.
168
+
169
+ ### Don't toggle the drawer just to peek
170
+
171
+ `muxr_drawer_read` works without showing the drawer. Toggling it to
172
+ look, then toggling back, is visible to the user as a flash of overlay
173
+ and is almost never what they wanted.
174
+
175
+ ### Tool errors
176
+
177
+ If a tool call returns `isError: true`, the text usually starts with
178
+ `muxr error <code>: <message>`. Common ones:
179
+
180
+ - `muxr error -32602: pane: no pane with id "…"` — the pane has been
181
+ killed, or you passed a stale id from before a kill/promote. Refetch
182
+ `muxr_panes_list`.
183
+ - `muxr error -32602: layout: unknown layout` — valid layouts are
184
+ `tall`, `grid`, `monocle`.
185
+
186
+ ## Naming muxr in conversation
187
+
188
+ When responding to the user, call panes by **slot first, id second**:
189
+ "pane #2 (a3f9b2) is showing the test failures." That matches what's on
190
+ their status bar and makes the id available for follow-up references.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: muxr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roel Bondoc
@@ -46,6 +46,7 @@ email:
46
46
  - rsbondoc@gmail.com
47
47
  executables:
48
48
  - muxr
49
+ - muxr-mcp
49
50
  extensions: []
50
51
  extra_rdoc_files: []
51
52
  files:
@@ -53,12 +54,16 @@ files:
53
54
  - LICENSE.txt
54
55
  - README.md
55
56
  - bin/muxr
57
+ - bin/muxr-mcp
56
58
  - lib/muxr.rb
57
59
  - lib/muxr/application.rb
58
60
  - lib/muxr/client.rb
59
61
  - lib/muxr/command_dispatcher.rb
62
+ - lib/muxr/control_server.rb
60
63
  - lib/muxr/drawer.rb
64
+ - lib/muxr/foreground_command.rb
61
65
  - lib/muxr/input_handler.rb
66
+ - lib/muxr/key_parser.rb
62
67
  - lib/muxr/layout_manager.rb
63
68
  - lib/muxr/pane.rb
64
69
  - lib/muxr/protocol.rb
@@ -69,6 +74,7 @@ files:
69
74
  - lib/muxr/version.rb
70
75
  - lib/muxr/window.rb
71
76
  - muxr.gemspec
77
+ - skills/muxr-control/SKILL.md
72
78
  homepage: https://github.com/roelbondoc/muxr
73
79
  licenses:
74
80
  - MIT