ace-tmux 0.14.3 → 0.17.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42e752d75968cab4e3af63e513247dd29b626e28f1ac0c88847c7fe036726df4
4
- data.tar.gz: 9b4aad5052e103fe757001d03d487cb221d4af0c989af41097c11d3db4cad1ea
3
+ metadata.gz: c4edb05780dda3c73b507989856230c358bc6b94be33bc27e34a71f0bd1a7d79
4
+ data.tar.gz: 679f06b6a332dbb3237c7383a6d61c665fd0bf65934a668ddc13fa1d46d86cfc
5
5
  SHA512:
6
- metadata.gz: 2f5de2adedeccbc4acb9cb1d876ececc32be10255b967daf764888b484d4af5fe02f26662f5979fc8978f0766f62f24faf0c32bb6915aeee3b42094235a3b001
7
- data.tar.gz: d1b4c69d036364e760299cde7ac47a3540d7889e51b4e053683d4b18e75afe39813bfdda22fbb30e5c4294153200006899b8f6e141fe58f3bbc2d1dc35e3a7a0
6
+ metadata.gz: 68d7a486f473d1095be471419db504421d53fa797ebaf7588c0273da6edb0c48bc24c050040662ab30941e253196343807f8db61997487d7ebbb555daa6474d5
7
+ data.tar.gz: da33e7b0921b5a0033827bee7aa1a9c40c3422d82591435f61e336bc61bb82d035029236c28b20e3bafa3ff2cf1a147d79758bcd71496c429a1f32945ca5a77f
data/CHANGELOG.md CHANGED
@@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.2] - 2026-04-24
11
+
12
+ ### Changed
13
+ - Clarified usage docs to treat speculative shared read-side follow-up as closed for now, so any future tmux inspectability expansion must start as a new task rather than reopening the archived `8r6.t.xeu` task family.
14
+
15
+ ### Fixed
16
+ - Clarified usage docs to treat `ace-tmux list` as the shipped read-side runtime inventory baseline and removed stale `ace-tmux state` references from capture and boundary notes.
17
+
18
+ ## [0.17.1] - 2026-04-24
19
+
20
+ ## [0.17.0] - 2026-04-16
21
+
22
+ ### Added
23
+ - Added a shared tmux runtime control surface with public `ace-tmux send`, `capture`, `wait`, `attach`, and `detach` commands.
24
+ - Added `send --wait` plus a new `agent` wait condition so interactive CLI panes, including shell-wrapped Codex/Claude/pi launches, can accept a prompt, block until the visible response settles, and then capture the final same-pane screen tail in one command.
25
+
26
+ ### Changed
27
+ - Added runtime target resolution, bounded named-key validation, and the v1 tmux wait-condition set for ACE-managed live control flows.
28
+ - Reworked `ace-tmux send` around `--cmd`, repeatable `--msg` / `--key`, and inline `--capture` output so one call can submit pane input and immediately tail feedback.
29
+ - Split preset discovery from runtime inspection: `ace-tmux --list-presets [TYPE]` now lists preset names, while `ace-tmux list` inspects live tmux panes in the current window by default and supports `--all-panes`, `--windows`, and `--sessions`.
30
+ - Tightened `ace-tmux send --wait output` so it matches post-send output instead of succeeding on stale visible text, and added `ace-tmux wait --lines` to control the observed pane tail.
31
+ - Expanded README, CLI help, docs, demo tape, and retained E2E coverage to document the control-side contract and the `--list-presets` plus runtime `list` split.
32
+
33
+ ### Fixed
34
+ - Qualified the default shared-control executor path so public `ace-tmux` control commands construct correctly without injected dependencies.
35
+ - Restored detached explicit-session active-window fallback so `ACE_TMUX_SESSION` launches can still resolve the current window outside a live tmux client.
36
+ - Validate `ace-tmux` pane targets before dispatch so invalid `session:window:pane` input now returns a corrective error that points users to `%pane_id`, `session:window.pane`, `.pane`, or `--window ... --pane ...`.
37
+ - Bound `capture-pane` with `-E -1` so `ace-tmux capture --lines N` and `ace-tmux send --capture N` return the last `N` visible pane rows instead of an oversized scrollback slice.
38
+ - Resolve pane shorthands through stable tmux window ids so `.pane`, bare pane indexes, and dotted window names like `ace-t.n1d` target the intended pane instead of producing invalid pane targets.
39
+ - Pace the first submit `Enter` for interactive CLI panes such as `codex`, `claude`, and `pi`, and capture their visible bottom screen rows so sends submit reliably and `--capture` reflects the current TUI screen instead of stale scrollback.
40
+ - `ace-tmux wait --for pane-exited` now succeeds when the pane disappears entirely instead of timing out on missing-pane targets.
41
+ - `ace-tmux detach` now reports the resolved target session name, even when the session came from ACE env vars or live tmux context.
42
+
43
+ ### Technical
44
+ - Added optional `split-window -P -F` command building so shared tmux consumers can capture the created pane id without private command arrays.
45
+
46
+ ## [0.14.6] - 2026-04-23
47
+
48
+ ### Technical
49
+ - Removed the stale cross-TC `session-name` fallback from retained existing-session verification so the goal now relies only on in-scope session continuity evidence.
50
+
51
+ ## [0.14.5] - 2026-04-23
52
+
53
+
54
+ ### Fixed
55
+ - Improved tmux session targeting so session-aware commands respect existing `TMUX` context instead of forcing socket-level targeting when already attached.
56
+ - Added regression coverage for active-session command paths that previously depended on socket-legacy behavior.
57
+
58
+
59
+ ### Fixed
60
+ - Fixed session targeting so commands now respect an active `TMUX` session and avoid forcing legacy `tmux -S` socket targeting when already inside tmux.
61
+
62
+ ## [0.14.4] - 2026-04-22
63
+
64
+ ### Fixed
65
+ - Sanitized ACE-managed tmux window names so path-derived names such as `ace-t.k5a` become tmux-safe targets like `ace-t-k5a`.
66
+ - Tracked startup windows by tmux window ID after creation so session startup selection no longer depends on raw or punctuation-bearing window names.
67
+
10
68
  ## [0.14.3] - 2026-04-16
11
69
 
12
70
  ### Technical
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
  <h1> ACE - TMUX </h1>
3
3
 
4
- Composable tmux sessions from YAML presets, with window injection into active sessions.
4
+ Composable tmux sessions from YAML presets, with window injection and shared control operations for active sessions.
5
5
 
6
6
  <img src="../docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
7
7
  <br><br>
@@ -16,13 +16,15 @@
16
16
 
17
17
  [Getting Started](docs/getting-started.md) | [Usage Guide](docs/usage.md) | [Handbook - Skills, Agents, Templates](docs/handbook.md)
18
18
 
19
- `ace-tmux` helps you standardize terminal workspaces with preset-driven sessions, reusable windows, and nested pane layouts. You can spin up a full workspace from a session preset or inject a focused window into an already-running tmux session, with config cascade across project, user, and gem defaults.
19
+ `ace-tmux` helps you standardize terminal workspaces with preset-driven sessions, reusable windows, nested pane layouts, runtime inspection, and a shared live-control surface for pane/session interaction. You can discover preset names with `--list-presets`, spin up a full workspace from a session preset, inspect the live tmux state with `list`, inject a focused window into an already-running tmux session, or use `send`/`capture`/`wait`/`attach`/`detach` as the public ACE tmux control contract.
20
20
 
21
21
  ## How It Works
22
22
 
23
23
  1. Define session and window layouts in YAML presets stored in `.ace/tmux/` (project), `~/.ace/tmux/` (user), or gem defaults.
24
24
  2. Run `ace-tmux start [PRESET]` to create a full session or `ace-tmux window [PRESET]` to inject a window into the current session. Use `ace-tmux start ... --name <session>` when you need multiple concurrent sessions from the same preset.
25
- 3. Presets are deep-merged through the config cascade, so project-level overrides layer cleanly on top of shared defaults.
25
+ 3. Use `ace-tmux list` to inspect panes in the current window, or widen the scope with `--all-panes`, `--windows`, or `--sessions`.
26
+ 4. Use `ace-tmux send`, `capture`, `wait`, `attach`, and `detach` for shared live tmux interaction without dropping to raw `tmux`.
27
+ 5. Presets are deep-merged through the config cascade, so project-level overrides layer cleanly on top of shared defaults.
26
28
 
27
29
  ## Use Cases
28
30
 
@@ -30,10 +32,16 @@
30
32
 
31
33
  **Inject windows into running sessions** - run `ace-tmux window [PRESET]` to add a new window from presets without recreating the current session, useful for spinning up focused tool or test panes on the fly.
32
34
 
35
+ **Inspect tmux state without raw tmux** - run `ace-tmux list` to inspect the current window’s panes, `ace-tmux list --windows` for the current session’s windows, or `ace-tmux list --sessions` for the tmux server view.
36
+
37
+ **Control tmux through a shared ACE contract** - run `ace-tmux send`, `capture`, `wait`, `attach`, and `detach` when higher-level ACE tools or operators need live pane/session control without re-implementing tmux wrappers.
38
+
33
39
  **Compose nested pane layouts in YAML** - use `direction` and nested pane containers to model custom split trees beyond tmux built-in layouts, keeping workspace structure version-controlled alongside your project.
34
40
 
35
41
  **Reuse presets through config cascade** - load project presets from `.ace/tmux/`, personal presets from `~/.ace/tmux/`, and gem defaults via `.ace-defaults/tmux/` with deep-merge behavior so teams share a baseline while individuals customize.
36
42
 
43
+ **Discover preset names cleanly** - run `ace-tmux --list-presets` with an optional `sessions`, `windows`, or `panes` filter when you need to inspect available presets without overloading the live-runtime `list` command.
44
+
37
45
  **Orchestrate task-focused workspaces** - pair with [ace-overseer](../ace-overseer) and [ace-assign](../ace-assign) for assignment-driven worktree and tmux orchestration that spins up isolated workspaces per task.
38
46
 
39
47
  ## Testing
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Showcase ace-tmux preset discovery, session start, and window management
2
+ description: Showcase ace-tmux preset discovery, runtime listing, session start, and window management
3
3
  tags:
4
4
  - ace-tmux
5
5
  - docs
@@ -11,6 +11,10 @@ settings:
11
11
  format: gif
12
12
  scenes:
13
13
  - name: Discover available presets (sessions, windows, panes)
14
+ commands:
15
+ - type: ace-tmux --list-presets
16
+ sleep: 4s
17
+ - name: Inspect panes in the current tmux window
14
18
  commands:
15
19
  - type: ace-tmux list
16
20
  sleep: 4s
@@ -3,8 +3,8 @@ doc-type: user
3
3
  title: ace-tmux Getting Started
4
4
  purpose: Tutorial for creating sessions and adding windows with ace-tmux.
5
5
  ace-docs:
6
- last-updated: 2026-03-22
7
- last-checked: 2026-03-22
6
+ last-updated: 2026-04-16
7
+ last-checked: 2026-04-16
8
8
  ---
9
9
 
10
10
  # Getting Started with ace-tmux
@@ -22,6 +22,8 @@ Verify command availability: `ace-tmux --help`.
22
22
 
23
23
  ## Start Your First Session
24
24
 
25
+ See available presets first with `ace-tmux --list-presets sessions`.
26
+
25
27
  Run: `ace-tmux start`.
26
28
 
27
29
  `ace-tmux` resolves the default session preset from `defaults.session` in config.
@@ -78,9 +80,11 @@ Use it in a session or add it directly with `ace-tmux window cc`.
78
80
 
79
81
  | Command | Purpose |
80
82
  | --- | --- |
83
+ | `ace-tmux --list-presets [TYPE]` | Show available preset names |
81
84
  | `ace-tmux start [PRESET]` | Start or attach to a session preset |
82
85
  | `ace-tmux window [PRESET]` | Add a window preset to a running session |
83
- | `ace-tmux list` | Show available session/window/pane presets |
86
+ | `ace-tmux list` | Show live panes in the current window |
87
+ | `ace-tmux list --windows` | Show windows in the current session |
84
88
 
85
89
  ## Next steps
86
90
 
data/docs/usage.md CHANGED
@@ -3,8 +3,8 @@ doc-type: user
3
3
  title: ace-tmux Usage
4
4
  purpose: Full CLI and configuration reference for ace-tmux.
5
5
  ace-docs:
6
- last-updated: 2026-03-22
7
- last-checked: 2026-03-22
6
+ last-updated: 2026-04-24
7
+ last-checked: 2026-04-24
8
8
  ---
9
9
 
10
10
  # Usage
@@ -13,7 +13,13 @@ ace-docs:
13
13
 
14
14
  - `ace-tmux start [PRESET] [OPTIONS]`
15
15
  - `ace-tmux window [PRESET] [OPTIONS]`
16
- - `ace-tmux list [TYPE] [OPTIONS]`
16
+ - `ace-tmux list [OPTIONS]`
17
+ - `ace-tmux --list-presets [TYPE]`
18
+ - `ace-tmux send [OPTIONS]`
19
+ - `ace-tmux capture [OPTIONS]`
20
+ - `ace-tmux wait [OPTIONS]`
21
+ - `ace-tmux attach [OPTIONS]`
22
+ - `ace-tmux detach [OPTIONS]`
17
23
 
18
24
  `PRESET` is optional for `start` and `window` when defaults are configured.
19
25
 
@@ -70,26 +76,173 @@ Behavior notes:
70
76
  - Inside tmux, current session is auto-detected.
71
77
  - Outside tmux, provide `--session` (or set `ACE_TMUX_SESSION`).
72
78
  - If no preset is provided, `defaults.window` is used.
79
+ - ACE-managed window names are normalized to `A-Z`, `a-z`, `0-9`, `_`, and `-`; other punctuation is replaced with `-`.
73
80
 
74
81
  ## `ace-tmux list`
75
82
 
76
- List available presets.
83
+ List live tmux sessions, windows, or panes.
77
84
 
78
85
  Examples:
79
86
 
80
87
  - `ace-tmux list`
81
- - `ace-tmux list sessions`
82
- - `ace-tmux list windows`
83
- - `ace-tmux list panes`
88
+ - `ace-tmux list --all-panes`
89
+ - `ace-tmux list --windows`
90
+ - `ace-tmux list --sessions`
91
+ - `ace-tmux list --session dev --window work`
92
+
93
+ Options:
94
+
95
+ - `--session`, `-s`: target session name
96
+ - `--window`, `-w`: target window name, index, or tmux window id (`@2`)
97
+ - `--all-panes`: list panes across the resolved session
98
+ - `--windows`: list windows in the resolved session
99
+ - `--sessions`: list all tmux sessions
100
+
101
+ Behavior notes:
102
+
103
+ - With no scope flags, `list` shows panes in the resolved current window.
104
+ - `--all-panes` expands from the current window to every window in the resolved session.
105
+ - `--windows` lists windows in the resolved session.
106
+ - `--sessions` lists tmux sessions and does not accept `--session` or `--window`.
107
+ - Runtime target resolution follows the same precedence as the control surface: explicit flags, then ACE tmux env vars, then live tmux context.
108
+
109
+ ## `ace-tmux --list-presets`
110
+
111
+ List available tmux preset names.
112
+
113
+ Examples:
114
+
115
+ - `ace-tmux --list-presets`
116
+ - `ace-tmux --list-presets sessions`
117
+ - `ace-tmux --list-presets windows`
84
118
 
85
119
  Arguments:
86
120
 
87
- - `TYPE`: one of `sessions`, `windows`, `panes`
121
+ - `TYPE`: optional `sessions`, `windows`, or `panes`
122
+
123
+ ## `ace-tmux send`
124
+
125
+ Send a submitted command, literal text, or bounded named keys to a target pane.
126
+
127
+ Examples:
128
+
129
+ - `ace-tmux send --pane %1 --cmd "bundle exec rake test"`
130
+ - `ace-tmux send --session dev --window work --pane 0 --cmd "ace-task status" --capture`
131
+ - `ace-tmux send --pane %8 --cmd "ping" --wait --capture 20`
132
+ - `ace-tmux send --pane %8 --cmd "Task context?" --wait agent --timeout 30 --capture`
133
+ - `ace-tmux send --pane .1 --msg "echo ready" --key Enter`
134
+ - `ace-tmux send --pane %1 --msg "echo ready" --key Enter`
135
+ - `ace-tmux send --pane %1 --key C-c`
136
+
137
+ Options:
138
+
139
+ - `--session`, `-s`: target session name
140
+ - `--window`, `-w`: target window name, index, or tmux window id (`@2`)
141
+ - `--pane`, `-p`: target pane id (`%8`), full pane target (`dev:work.1`), or current-window pane shorthand (`.1`)
142
+ - `--cmd`, `-c`: command text to send and submit with `Enter`
143
+ - `--msg`, `-m`: literal text chunk to send without `Enter`
144
+ - `--key`, `-k`: named key to send
145
+ - `--capture [lines[:wait]]`: capture recent pane output after sending; defaults to `40:2`
146
+ - `--wait [condition]`: wait after send; bare `--wait` defaults to `agent`
147
+ - `--pattern`: pattern to match when waiting for `output`
148
+ - `--timeout`, `-t`: wait timeout in seconds when `--wait` is used
149
+ - `--interval`, `-i`: wait polling interval in seconds when `--wait` is used
150
+ - `--quiet`, `-q`: suppress non-essential output
151
+
152
+ Behavior notes:
153
+
154
+ - Provide at least one of `--cmd`, `--msg`, or `--key`.
155
+ - Use either `--cmd` or `--msg`, not both.
156
+ - Repeated `--msg` values are sent in order, then any implicit `Enter` from `--cmd`, then repeated `--key` values in order.
157
+ - `--cmd` always appends one final `Enter`.
158
+ - Embedded newlines in `--cmd` or `--msg` remain literal text; they are not expanded into extra key presses.
159
+ - Named keys are intentionally bounded; unsupported raw tmux key syntax is rejected.
160
+ - Interactive CLI panes such as `codex`, `claude`, and `pi` get a brief automatic pause before the first submit `Enter` after text so the TUI sees a submit key instead of a paste burst.
161
+ - `--wait agent` blocks until a supported interactive CLI pane visibly changes and then settles; bare `--wait` is shorthand for that main send-and-wait flow.
162
+ - `send --wait output` compares against the pre-send pane tail, so existing visible matches do not satisfy the wait unless the pattern appears again after the send.
163
+ - Interactive CLI detection also covers panes launched through shell wrappers, so detached `fish -c codex` / similar panes still use the interactive send, wait, and capture path.
164
+ - When `--wait` and `--capture` are used together, ACE waits first and then captures from the same pane, so the printed tail reflects the settled post-response screen.
165
+ - Target resolution uses one precedence rule across the control surface: explicit flags, then ACE tmux env vars, then live tmux context.
166
+ - `--capture N` prints the visible bottom `N` rows for interactive CLI panes and a recent history tail for generic shell panes.
167
+ - Supported pane forms are `%8`, `dev:work.1`, `.1`, and bare `1`. `dev:work:1` is invalid; use `dev:work.1` instead.
168
+ - `.1` and bare `1` both resolve against the explicit or resolved current window.
169
+ - Window names may contain dots. ACE resolves pane shorthands through tmux window ids internally, so `--window ace-t.n1d --pane .3` remains valid.
170
+
171
+ ## `ace-tmux capture`
172
+
173
+ Capture recent output from a tmux pane.
174
+
175
+ Examples:
176
+
177
+ - `ace-tmux capture --pane %1`
178
+ - `ace-tmux capture --pane %1 --lines 80`
179
+
180
+ Options:
181
+
182
+ - `--session`, `-s`: target session name
183
+ - `--window`, `-w`: target window name, index, or tmux window id (`@2`)
184
+ - `--pane`, `-p`: target pane id (`%8`), full pane target (`dev:work.1`), or current-window pane shorthand (`.1`)
185
+ - `--lines`, `-n`: number of recent lines to capture
186
+
187
+ Behavior notes:
188
+
189
+ - Capture is a live control-side pane-tail operation, not a generic read-side runtime inventory surface.
190
+ - `--lines N` captures the visible bottom `N` rows for interactive CLI panes such as `codex`, `claude`, and `pi`.
191
+ - Generic shell panes keep using a recent history tail instead of the current visible screen.
192
+ - Interactive CLI captures may include visible draft/composer text when it is on screen.
193
+ - Pane targeting follows the same rules as `send`: `%8`, `dev:work.1`, `.1`, or bare `1`.
194
+
195
+ ## `ace-tmux wait`
196
+
197
+ Wait for a bounded tmux condition.
198
+
199
+ Examples:
200
+
201
+ - `ace-tmux wait --pane %1 --for output --pattern "Task context:"`
202
+ - `ace-tmux wait --pane %8 --for agent`
203
+ - `ace-tmux wait --session dev --for window-active --window work-fs`
204
+ - `ace-tmux wait --pane %1 --for pane-exited`
88
205
 
89
206
  Options:
90
207
 
91
- - `--verbose`, `-v`
92
- - `--quiet`, `-q`
208
+ - `--for`, `-f`: one of `agent`, `output`, `window-exists`, `window-active`, `pane-exists`, `pane-exited`
209
+ - `--session`, `-s`: target session name
210
+ - `--window`, `-w`: target window name, index, or tmux window id (`@2`)
211
+ - `--pane`, `-p`: target pane id (`%8`), full pane target (`dev:work.1`), or current-window pane shorthand (`.1`)
212
+ - `--pattern`: output substring to match
213
+ - `--lines`, `-n`: number of lines to observe for `output` and `agent` waits
214
+ - `--timeout`, `-t`: timeout in seconds
215
+ - `--interval`, `-i`: polling interval in seconds
216
+ - `--quiet`, `-q`: suppress non-essential output
217
+
218
+ Behavior notes:
219
+
220
+ - `--for agent` waits for a supported interactive CLI pane such as `codex`, `claude`, or `pi` to become visibly idle/stable, including panes launched through wrapper shells.
221
+ - `--pattern` is required for `--for output`.
222
+ - `--for output` observes the last `--lines` lines of the target pane.
223
+ - `--for pane-exited` succeeds when the pane reports `pane_dead=1` or disappears entirely.
224
+ - Wait timeouts fail clearly; they do not silently fall through.
225
+ - Pane targeting follows the same rules as `send`: `%8`, `dev:work.1`, `.1`, or bare `1`.
226
+
227
+ ## `ace-tmux attach`
228
+
229
+ Attach to a tmux session using the same shared target-resolution rules.
230
+
231
+ Examples:
232
+
233
+ - `ace-tmux attach --session dev`
234
+
235
+ ## `ace-tmux detach`
236
+
237
+ Detach clients from a tmux session.
238
+
239
+ Examples:
240
+
241
+ - `ace-tmux detach --session dev`
242
+
243
+ Behavior notes:
244
+
245
+ - `detach` removes attached clients from the target session and is intended to replace raw `tmux detach-client -s ...` usage in ACE-owned flows.
93
246
 
94
247
  ## Config Cascade
95
248
 
@@ -200,6 +353,11 @@ Chained preset references are supported (depth-limited).
200
353
  - [Getting Started](getting-started.md)
201
354
  - [Handbook Reference](handbook.md)
202
355
 
356
+ ## Boundary Notes
357
+
358
+ - `ace-tmux` control commands (`send`, `capture`, `wait`, `attach`, `detach`) define the live interaction surface.
359
+ - Read-side runtime inventory currently uses the shipped `ace-tmux list` surface. No additional shared read-side CLI follow-up is active today; any future consumer-driven expansion should start as a new task rather than reopening the archived `8r6.t.xeu` family.
360
+
203
361
  ## Testing Contract
204
362
 
205
363
  - `ace-test ace-tmux` validates deterministic fast coverage in `test/fast/`.
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Tmux
5
+ module Atoms
6
+ module NamedKeyRegistry
7
+ module_function
8
+
9
+ KEYS = {
10
+ "enter" => "Enter",
11
+ "tab" => "Tab",
12
+ "space" => "Space",
13
+ "escape" => "Escape",
14
+ "esc" => "Escape",
15
+ "up" => "Up",
16
+ "down" => "Down",
17
+ "left" => "Left",
18
+ "right" => "Right",
19
+ "c-c" => "C-c"
20
+ }.freeze
21
+
22
+ def normalize(key)
23
+ value = key.to_s.strip
24
+ raise Ace::Tmux::ValidationError, "Named key is required" if value.empty?
25
+
26
+ normalized = KEYS[value.downcase]
27
+ raise Ace::Tmux::ValidationError, "Unsupported named key: #{key}" unless normalized
28
+
29
+ normalized
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -63,11 +63,12 @@ module Ace
63
63
  # @param horizontal [Boolean] Split horizontally (default: vertically)
64
64
  # @param tmux [String] tmux binary path
65
65
  # @return [Array<String>]
66
- def split_window(target, root: nil, horizontal: false, tmux: "tmux")
66
+ def split_window(target, root: nil, horizontal: false, print_format: nil, tmux: "tmux")
67
67
  cmd = [tmux, "split-window"]
68
68
  cmd << "-h" if horizontal
69
69
  cmd.concat(["-t", target])
70
70
  cmd.concat(["-c", File.expand_path(root)]) if root
71
+ cmd.concat(["-P", "-F", print_format]) if print_format
71
72
  cmd
72
73
  end
73
74
 
@@ -80,6 +81,10 @@ module Ace
80
81
  [tmux, "send-keys", "-t", target, keys, "Enter"]
81
82
  end
82
83
 
84
+ def send_raw_keys(target, *keys, tmux: "tmux")
85
+ [tmux, "send-keys", "-t", target, *keys]
86
+ end
87
+
83
88
  # Set the layout for a window
84
89
  # @param target [String] Target window
85
90
  # @param layout [String] Layout name (e.g., "main-vertical", "tiled")
@@ -131,6 +136,12 @@ module Ace
131
136
  cmd
132
137
  end
133
138
 
139
+ def list_windows(target, format: nil, tmux: "tmux")
140
+ cmd = [tmux, "list-windows", "-t", target]
141
+ cmd.concat(["-F", format]) if format
142
+ cmd
143
+ end
144
+
134
145
  # Set a window option
135
146
  # @param target [String] Target window
136
147
  # @param option [String] Option name (e.g., "main-pane-width")
@@ -197,6 +208,22 @@ module Ace
197
208
  cmd.concat(["-F", format]) if format
198
209
  cmd
199
210
  end
211
+
212
+ def capture_pane(target, lines: 40, tmux: "tmux")
213
+ start = -Integer(lines.to_i.abs)
214
+ [tmux, "capture-pane", "-p", "-t", target, "-S", start.to_s, "-E", "-1"]
215
+ end
216
+
217
+ def capture_pane_visible(target, start_line:, end_line:, include_alternate: false, tmux: "tmux")
218
+ cmd = [tmux, "capture-pane", "-p"]
219
+ cmd << "-a" if include_alternate
220
+ cmd.concat(["-t", target, "-S", Integer(start_line).to_s, "-E", Integer(end_line).to_s])
221
+ cmd
222
+ end
223
+
224
+ def detach_client(session, tmux: "tmux")
225
+ [tmux, "detach-client", "-s", session]
226
+ end
200
227
  end
201
228
  end
202
229
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Tmux
5
+ module Atoms
6
+ # Normalizes ACE-managed tmux window names to avoid target parsing ambiguity.
7
+ module WindowNameSanitizer
8
+ module_function
9
+
10
+ def call(value, fallback: "window")
11
+ sanitized = sanitize(value)
12
+ sanitized.empty? ? sanitize(fallback).then { |name| name.empty? ? "window" : name } : sanitized
13
+ end
14
+
15
+ def sanitize(value)
16
+ value.to_s
17
+ .gsub(/[^A-Za-z0-9_-]+/, "-")
18
+ .gsub(/-+/, "-")
19
+ .gsub(/\A-|-+\z/, "")
20
+ end
21
+ private_class_method :sanitize
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require "ace/core"
5
+
6
+ module Ace
7
+ module Tmux
8
+ module CLI
9
+ module Commands
10
+ class Attach < Ace::Support::Cli::Command
11
+ include Ace::Support::Cli::Base
12
+
13
+ desc "Attach to a tmux session using shared target resolution"
14
+
15
+ option :session, type: :string, aliases: %w[-s], desc: "Target session name"
16
+
17
+ def call(**options)
18
+ Organisms::ControlSurface.new.attach_session(session: options[:session])
19
+ rescue Ace::Tmux::Error => e
20
+ raise Ace::Support::Cli::Error, e.message
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require "ace/core"
5
+
6
+ module Ace
7
+ module Tmux
8
+ module CLI
9
+ module Commands
10
+ class Capture < Ace::Support::Cli::Command
11
+ include Ace::Support::Cli::Base
12
+
13
+ desc <<~DESC.strip
14
+ Capture recent output from a tmux pane
15
+
16
+ Interactive CLI panes capture the visible bottom of the current screen.
17
+ Generic shell panes capture a recent history tail.
18
+ DESC
19
+
20
+ example [
21
+ "--pane %1",
22
+ "--pane %8 --lines 20",
23
+ "--session dev --window work --pane .1 --lines 10"
24
+ ]
25
+
26
+ option :session, type: :string, aliases: %w[-s], desc: "Target session name"
27
+ option :window, type: :string, aliases: %w[-w], desc: "Target window name, index, or tmux window id (@2)"
28
+ option :pane, type: :string, aliases: %w[-p], desc: "Target pane id (%8), full pane target (dev:work.1), or current-window pane shorthand (.1)"
29
+ option :lines, type: :integer, aliases: %w[-n], default: 40, desc: "Number of recent lines to capture"
30
+
31
+ def call(**options)
32
+ output = Organisms::ControlSurface.new.capture_recent_output(**options.slice(:session, :window, :pane, :lines))
33
+ puts output
34
+ rescue Ace::Tmux::Error => e
35
+ raise Ace::Support::Cli::Error, e.message
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require "ace/core"
5
+
6
+ module Ace
7
+ module Tmux
8
+ module CLI
9
+ module Commands
10
+ class Detach < Ace::Support::Cli::Command
11
+ include Ace::Support::Cli::Base
12
+
13
+ desc "Detach clients from a tmux session"
14
+
15
+ option :session, type: :string, aliases: %w[-s], desc: "Target session name"
16
+ option :quiet, type: :boolean, aliases: %w[-q], desc: "Suppress non-essential output"
17
+
18
+ def call(**options)
19
+ session = Organisms::ControlSurface.new.detach_session(session: options[:session])
20
+ puts "Detached session #{session}" unless options[:quiet]
21
+ rescue Ace::Tmux::Error => e
22
+ raise Ace::Support::Cli::Error, e.message
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end