diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-25 07:44:25 +0200 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-25 07:44:25 +0200 |
| commit | 5d7ed0172366eef885fd490136e8b826a9722339 (patch) | |
| tree | 26e7bcb2b21d190072b66e3349a14cbdf8382a62 /crates | |
| parent | 5fe10900102bb2e083f41ccafff25c4b3e378cd8 (diff) | |
terminal commands are silent when tmux runs them
tmux shows a hook's output and a non-zero exit in view mode over the pane.
the focus hook's normal outcomes (focus moved on, Ghostty not in front)
exited 1, and the one-second settling made "focus moved on" common:
"'dingger terminal focused --client ...' returned 1" kept covering panes.
`dingger terminal focused` and `terminal raise` now always exit 0 and
print nothing unless run by hand.
tmux::client_format checks that tmux answered for the client asked about:
for a client that is gone, tmux answers for the current one, so a raise
for a client that had just detached raised another client's terminal.
e2e step 20: no pane in view mode after the hook's failures, a gone
client raises nothing, and a focus held past the settling second; the old
code fails the new checks.
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/dinggerd/src/main.rs | 17 | ||||
| -rw-r--r-- | crates/dinggerd/src/tmux.rs | 10 |
2 files changed, 20 insertions, 7 deletions
diff --git a/crates/dinggerd/src/main.rs b/crates/dinggerd/src/main.rs index e43f9a7..b97471c 100644 --- a/crates/dinggerd/src/main.rs +++ b/crates/dinggerd/src/main.rs @@ -13,6 +13,7 @@ mod tmux; mod watcher; use clap::{Parser, Subcommand}; +use std::io::IsTerminal; use tracing_subscriber::EnvFilter; #[derive(Parser)] @@ -128,7 +129,14 @@ fn main() { let cli = Cli::parse(); // The daemon logs its work; commands print their own output and only // log problems. - let default_level = if matches!(cli.command, Command::Watch { .. }) { "info" } else { "warn" }; + // tmux shows whatever its hooks print, and a non-zero exit, as a + // message: the commands tmux runs are silent unless run by hand. + let by_tmux = matches!(cli.command, Command::Terminal { .. }) && !std::io::stderr().is_terminal(); + let default_level = match cli.command { + Command::Watch { .. } => "info", + _ if by_tmux => "off", + _ => "warn", + }; tracing_subscriber::fmt() .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| default_level.into())) .init(); @@ -197,9 +205,12 @@ fn main() { TerminalAction::Focused { client } => terminals::learn(client), TerminalAction::Raise { client } => terminals::raise(client), }; + // Best effort, run by tmux (the focus hook, a raise): an outcome + // like "focus moved on" or "terminal gone" is not a failure. if let Err(err) = result { - eprintln!("dingger: {err}"); - std::process::exit(1); + if !by_tmux { + eprintln!("dingger: {err}"); + } } } // Never fails the agent: exits 0, prints nothing. diff --git a/crates/dinggerd/src/tmux.rs b/crates/dinggerd/src/tmux.rs index 54da39d..f69acd1 100644 --- a/crates/dinggerd/src/tmux.rs +++ b/crates/dinggerd/src/tmux.rs @@ -274,10 +274,12 @@ pub fn pane_format(pane_id: &str, format: &str) -> Option<String> { /// Expand a format in the context of a client (its current pane/window). pub fn client_format(client: &str, format: &str) -> Option<String> { - run_tmux(&["display-message", "-p", "-c", client, format]) - .ok() - .map(|value| value.trim().to_string()) - .filter(|value| !value.is_empty()) + // For a client that is gone, tmux answers for the current one instead: + // the answer names its client, and must name this one. + let out = run_tmux(&["display-message", "-p", "-c", client, &format!("#{{client_name}}\t{format}")]).ok()?; + let (name, value) = out.trim_end_matches('\n').split_once('\t')?; + let value = value.trim(); + (name == client && !value.is_empty()).then(|| value.to_string()) } /// The sessions holding a pane's window (several: a session group, a linked |
