From 49901d7f8a3addcef67b7fb47feb37152b4e991e Mon Sep 17 00:00:00 2001 From: l0ng-ai Date: Thu, 13 Aug 2026 11:43:17 +0800 Subject: [PATCH] fix(cli): let --enter press the key it is shorthand for (#581) (#606) `--enter` is documented as sugar for `--key enter`, but the send dispatch counted only `args.keys`, so `tty7 send %42 --enter` answered "needs TEXT ... or a --key to press" and pressed nothing. The key list is now built before the dispatch and the dispatch counts it, so a marked address with `--enter` and nothing else runs what the pane already has typed, and a bare `send --enter` presses Enter where the caller sits. An unmarked id is deliberately left out of that promotion. #567 made the address slot take bare ids, and `send 83 --key C-c` addressing pane 83 is fine because `--key` says "press this" and nothing else. `--enter` does not: `send 2 --enter` reads at least as much like typing 2 into your own pane and running it, and turning it into a keystroke at pane 2 would be the silent retarget #567 spent its diff closing. It stays a loud error, now naming both spellings (`send %83 --enter`, `send %PANE 83 --enter`) rather than only the typing one. The reference, the bundled skill reference, `send --help` and the `--enter` help all said the old thing in slightly different words; they now say the same thing as each other and as the code. Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> --- CHANGELOG.md | 10 +++ crates/tty7-cli/src/cli.rs | 6 +- crates/tty7-cli/src/commands.rs | 125 ++++++++++++++++++++++------- crates/tty7-cli/src/keys.rs | 4 + docs/cli/reference.mdx | 23 +++--- skills/tty7/SKILL.md | 6 +- skills/tty7/references/commands.md | 23 +++--- 7 files changed, 148 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a70b1efc..af5fb409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the arguments that need it, and a value whose quotes do not close is refused with an explanation under the input rather than saved as fragments. A path spelled with backslashes still means itself. (#551) +- **`tty7 send --enter` now presses Enter when there is nothing to type** — + `--enter` is shorthand for `--key enter`, but it was never counted as a key, + so `tty7 send %42 --enter` answered "needs TEXT … or a --key to press" + instead of running what pane 42 already had typed. It counts now, with an + address or without one (`tty7 send --enter` presses Enter in your own pane). + An *unmarked* id is deliberately left out: `tty7 send 83 --enter` reads as + much like typing "83" where you are sitting as like pressing Enter in pane + 83, so it stays a loud error that names both spellings (`send %83 --enter`, + `send %PANE 83 --enter`) rather than quietly retargeting the keystroke + (#581). ## [26.8.3] - 2026-08-12 diff --git a/crates/tty7-cli/src/cli.rs b/crates/tty7-cli/src/cli.rs index 6e6daa6c..56d0b234 100644 --- a/crates/tty7-cli/src/cli.rs +++ b/crates/tty7-cli/src/cli.rs @@ -202,7 +202,11 @@ pub struct SendArgs { #[arg(value_name = "TEXT")] pub second: Option, - #[arg(long, help = "Press Enter after the text")] + #[arg( + long, + help = "Press Enter after the text, or on its own when there is none \ + (= --key enter)" + )] pub enter: bool, // Text covers "type this command"; it cannot express the keystrokes a pane diff --git a/crates/tty7-cli/src/commands.rs b/crates/tty7-cli/src/commands.rs index d07b0942..a787149e 100644 --- a/crates/tty7-cli/src/commands.rs +++ b/crates/tty7-cli/src/commands.rs @@ -484,11 +484,25 @@ fn pane_split(args: SplitArgs, ctx: &Context, backend: &mut dyn Backend) -> Resu fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result { const KEY_GAP: Duration = Duration::from_millis(200); + // `--enter` is the same thing as `--key enter`, and predates it. Keeping it + // as sugar rather than deprecating it: it reads better for the overwhelming + // case, which is typing one command and running it. Going through the same + // parser leaves one definition of what Enter puts on the wire — and the list + // is built here, before the dispatch, because the dispatch has to count it: + // `send %42 --enter` used to report "needs TEXT … or a --key to press" while + // the docs called `--enter` shorthand for exactly such a key (#581). + let mut pressed = args.keys.clone(); + if args.enter { + pressed.push(crate::keys::parse("enter").expect("enter is in the vocabulary")); + } + // Three shapes reach here, and only the address is ever ambiguous: // `send %3 "text"`, `send "text"` (this pane), and — new with --key — // `send %3 --key C-c`, where there is no text at all and the lone // positional is therefore an address rather than the missing-text error it - // has to stay in every other case. + // has to stay in every other case. `send %3 --enter` is that third shape + // too, with the one carve-out below: only a marked address is promoted by + // `--enter` alone. // // The address-shaped-but-broken case must not fall through to "type it": // `send %3x --key C-c` used to type `%3x` into the *caller's own* pane and @@ -501,12 +515,26 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result (Some(first.as_str()), Some(text.as_str())), (Some(first), None) => match address::parse_pane(first) { Ok(_) => { - if args.keys.is_empty() { + if pressed.is_empty() { bail!( "send needs TEXT after the pane address, or a --key to press \ — to type '{first}' literally, name the pane too: send %PANE {first}" ); } + // A `--key` is always an explicit "press this", so it promotes + // either spelling of the address. `--enter` is not, for an + // *unmarked* id: `send 2 --enter` reads as "type 2 and run it" + // far more often than "press Enter in pane 2", and #538 was + // about never quietly retargeting a keystroke. The `%` is what + // says which was meant, so it stays the loud error it is today. + if args.keys.is_empty() && !first.starts_with('%') { + bail!( + "'{first}' is a bare pane id and --enter has nothing to type \ + — to press Enter in pane {first}: send %{first} --enter; \ + to type '{first}' and press Enter, name the pane too: \ + send %PANE {first} --enter" + ); + } (Some(first.as_str()), None) } // `%` then a digit is someone writing an address, so the parse @@ -515,7 +543,7 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result (None, Some(first.as_str())), }, (None, _) => { - if args.keys.is_empty() { + if pressed.is_empty() { bail!("send needs TEXT to type or a --key to press"); } (None, None) @@ -528,14 +556,6 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result String { --key sends a keystroke rather than characters, which is what a pane wants once \ something is already running in it: answering a prompt that only takes arrow keys, \ closing a TUI with escape, stopping a build with C-c. Repeat it for a sequence.\n\n\ + --enter is shorthand for --key enter: it presses Enter after TEXT, or on its own \ + when there is none, so `send %42 --enter` runs whatever is already typed in pane 42. \ + An unmarked id is not a target for it — `send 83 --enter` is refused, because it \ + reads just as much like typing 83 into your own pane; write %83 to mean the pane.\n\n\ Keys: {}. Aliases: {}.", vocabulary(), aliases.join(", ") diff --git a/docs/cli/reference.mdx b/docs/cli/reference.mdx index 29c8ef78..123fbe63 100644 --- a/docs/cli/reference.mdx +++ b/docs/cli/reference.mdx @@ -93,15 +93,20 @@ share kept by the *existing* pane. Prints `%NN`. JSON: `{"pane"}`. ### `tty7 send [%PANE] [TEXT] [--enter] [--key KEY]…` -Types `TEXT` into the pane as keystrokes; `--enter` appends CR. With one -argument the text is the argument and the pane comes from `$TTY7_PANE` — but a -lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected as a -missing-text error rather than typed, unless a `--key` gives it something to -do. A `%` followed by a digit that still doesn't parse (`%3x`) is an address -error, never text for your own pane — while text that merely starts with `%` -(`%s/foo/bar/`, `%!sort`) types as given, as does anything unmarked that is not -a plain number (`3x`, `+5`). To type an address-shaped string, name the pane as -well: `tty7 send %42 %3x`. +Types `TEXT` into the pane as keystrokes; `--enter` is shorthand for `--key +enter` — it appends CR to the text, or presses Enter on its own when there is +none, so `tty7 send %42 --enter` runs whatever pane 42 already has typed. With +one argument the text is the argument and the pane comes from `$TTY7_PANE` — +but a lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected +as a missing-text error rather than typed, unless a `--key` gives it something +to do. `--enter` is that key only for the `%`-marked spelling: `tty7 send 83 +--enter` is refused, because it reads as much like typing `83` into your own +pane as like pressing Enter in pane 83, and the error names both ways to say +which (`send %83 --enter`, `send %PANE 83 --enter`). A `%` followed by a digit +that still doesn't parse (`%3x`) is an address error, never text for your own +pane — while text that merely starts with `%` (`%s/foo/bar/`, `%!sort`) types +as given, as does anything unmarked that is not a plain number (`3x`, `+5`). To +type an address-shaped string, name the pane as well: `tty7 send %42 %3x`. `--key` presses a key instead of typing characters, which is what a pane wants once something is already running in it: answering a prompt that only takes diff --git a/skills/tty7/SKILL.md b/skills/tty7/SKILL.md index 1835fb33..354a2a86 100644 --- a/skills/tty7/SKILL.md +++ b/skills/tty7/SKILL.md @@ -118,8 +118,10 @@ read -r WS PANE < <(tty7 new --json /path/to/repo \ ``` `send` types text into the pane exactly as a keyboard would; `--enter` appends -the carriage return. It does not wait and it does not tell you what happened — -reading is a separate step, and waiting is `tty7 wait`. +the carriage return, or presses Enter on its own when you give it no text +(`tty7 send "$PANE" --enter` runs what is already typed there). It does not +wait and it does not tell you what happened — reading is a separate step, and +waiting is `tty7 wait`. For keystrokes rather than characters — Ctrl-C, Escape, the arrow keys — use `--key` (see [Answering a prompt](#answering-a-prompt)). Typing `^C` as text diff --git a/skills/tty7/references/commands.md b/skills/tty7/references/commands.md index 7186bd33..c8f83a1e 100644 --- a/skills/tty7/references/commands.md +++ b/skills/tty7/references/commands.md @@ -100,15 +100,20 @@ pane below, `--h`/`--horizontal` to the right. `--ratio` (default 0.5) is the share kept by the *existing* pane. Prints `%NN`. JSON: `{"pane"}`. ### `tty7 send [%PANE] [TEXT] [--enter] [--key KEY]…` -Types `TEXT` into the pane as keystrokes; `--enter` appends CR. With one -argument the text is the argument and the pane comes from `$TTY7_PANE` — but a -lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected as a -missing-text error rather than typed, unless a `--key` gives it something to -do. A `%` followed by a digit that still doesn't parse (`%3x`) is an address -error, never text for your own pane — while text that merely starts with `%` -(`%s/foo/bar/`, `%!sort`) types as given, as does anything unmarked that is not -a plain number (`3x`, `+5`). To type an address-shaped string, name the pane as -well: `tty7 send %42 %3x`. +Types `TEXT` into the pane as keystrokes; `--enter` is shorthand for `--key +enter` — it appends CR to the text, or presses Enter on its own when there is +none, so `tty7 send %42 --enter` runs whatever pane 42 already has typed. With +one argument the text is the argument and the pane comes from `$TTY7_PANE` — +but a lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected +as a missing-text error rather than typed, unless a `--key` gives it something +to do. `--enter` is that key only for the `%`-marked spelling: `tty7 send 83 +--enter` is refused, because it reads as much like typing `83` into your own +pane as like pressing Enter in pane 83, and the error names both ways to say +which (`send %83 --enter`, `send %PANE 83 --enter`). A `%` followed by a digit +that still doesn't parse (`%3x`) is an address error, never text for your own +pane — while text that merely starts with `%` (`%s/foo/bar/`, `%!sort`) types +as given, as does anything unmarked that is not a plain number (`3x`, `+5`). To +type an address-shaped string, name the pane as well: `tty7 send %42 %3x`. JSON: `{"pane","sent","enter","keys"}`. `--key` presses a key instead of typing characters — the arrow keys a