Files
tty7/crates/tty7-cli/src/main.rs
T
l0ng-ai 02d8611e33 feat(cli): add exec, and send --stdin/--from-file/--paste (#951)
`tty7 exec %N -- CMD` types a line at an existing pane's shell prompt,
follows the shell integration's OSC 133 marks to the command's end, prints
what it printed (rendered to text by default, `--raw` for the bytes) and
exits with its exit code. `--timeout` exits 124 and leaves the command
running. A pane with no prompt marks, or one not at a prompt, is refused
before anything is typed.

`tty7 send` can take its text from `--stdin` or `--from-file`, sent byte
for byte and never echoed in --json, so a secret stays out of `ps` and
shell history. `--paste` frames the text the way the GUI's paste does:
bracketed when the pane has mode 2004 on, unframed otherwise, reported in
--json. The framing moves into tty7-core (`core::paste`) so the GUI's
clipboard paste, the agent prompt and the CLI share one construction, and
the daemon now reports each pane's bracketed-paste mode in PaneContext.

Closes #839
Closes #838
2026-09-25 16:32:19 +08:00

67 lines
1.8 KiB
Rust

mod address;
mod backend;
mod cli;
mod commands;
mod exec;
mod gui;
mod keys;
mod output;
mod resolve;
mod screen;
mod server;
mod stdio;
#[cfg(test)]
mod testbed;
use clap::Parser;
fn main() -> std::process::ExitCode {
// Before the first byte goes out, and before clap can print a usage error.
stdio::end_pipelines_quietly();
let cli = cli::Cli::parse();
let json = cli.json;
let quiet = cli.quiet;
let ctx = address::Context::from_env();
let mut backend = backend::RealBackend::new(cli.machine.clone());
match commands::execute(cli, &ctx, &mut backend) {
// `run` stands in for the command it launched, so its exit code is
// ours. The report rides along anyway: --json must not go silent just
// because the verb also carries an exit code.
Ok(commands::Outcome::Exit(code, report)) => {
// No flush needed before the exit below: `stdio::out` already did.
emit(report, json, quiet);
std::process::exit(code)
}
Ok(commands::Outcome::Report(report)) => {
emit(report, json, quiet);
std::process::ExitCode::SUCCESS
}
// --quiet suppresses output on success, not the reason for a failure:
// swallowing this leaves a bare exit code and nothing to debug.
Err(e) => {
eprintln!("tty7: {e:#}");
std::process::ExitCode::FAILURE
}
}
}
fn emit(report: commands::Report, json: bool, quiet: bool) {
if quiet {
return;
}
if json {
if !report.json.is_null() {
stdio::line(&report.json.to_string());
}
} else if !report.human.is_empty() {
stdio::out(ensure_newline(report.human).as_bytes());
}
}
fn ensure_newline(mut s: String) -> String {
if !s.ends_with('\n') {
s.push('\n');
}
s
}