Fix auto-lookback trigger and add version tracking (#20)

* Fix auto-lookback trigger and add version tracking

- Trigger on stdin inactivity instead of render time
- Only dump when new output exists since last dump
- Default timeout 5s -> 15s
- Embed git hash in version output
- Document macOS config path
This commit is contained in:
David Beesley
2026-01-22 15:58:47 -07:00
committed by GitHub
parent 35ecc80e2f
commit 0903d8c205
8 changed files with 101 additions and 18 deletions

View File

@@ -0,0 +1,38 @@
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/index");
let git_hash = get_git_hash();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
}
fn get_git_hash() -> String {
let hash = match Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
{
Ok(output) if output.status.success() => String::from_utf8(output.stdout)
.ok()
.map(|s| s.trim().to_string()),
_ => None,
};
let Some(hash) = hash else {
return "git not found during build, version unavailable".to_string();
};
let is_dirty = Command::new("git")
.args(["status", "--porcelain"])
.output()
.ok()
.map(|output| !output.stdout.is_empty())
.unwrap_or(false);
if is_dirty {
format!("{}-dirty", hash)
} else {
hash
}
}

View File

@@ -1,9 +1,11 @@
use clap::Parser;
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", env!("GIT_HASH"), ")");
#[derive(Parser, Debug)]
#[command(
name = "claude-chill",
version,
version = VERSION,
about = "A PTY proxy that tames Claude Code's massive terminal updates"
)]
pub struct Cli {
@@ -22,7 +24,7 @@ pub struct Cli {
#[arg(short = 'k', long = "lookback-key")]
pub lookback_key: Option<String>,
/// Auto-lookback timeout in ms, 0 to disable (default: 5000)
/// Auto-lookback timeout in ms, 0 to disable (default: 15000)
#[arg(short = 'a', long = "auto-lookback-timeout")]
pub auto_lookback_timeout: Option<u64>,
}

View File

@@ -5,7 +5,7 @@ use std::path::PathBuf;
const DEFAULT_LOOKBACK_KEY: &str = "[ctrl][6]";
const DEFAULT_REFRESH_RATE: u64 = 20;
const DEFAULT_AUTO_LOOKBACK_TIMEOUT_MS: u64 = 5000;
const DEFAULT_AUTO_LOOKBACK_TIMEOUT_MS: u64 = 15000;
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
@@ -100,7 +100,7 @@ mod tests {
assert_eq!(config.lookback_key, "[ctrl][6]");
assert_eq!(config.refresh_rate, 20);
assert_eq!(config.redraw_throttle_ms(), 50);
assert_eq!(config.auto_lookback_timeout_ms, 5000);
assert_eq!(config.auto_lookback_timeout_ms, 15000);
}
#[test]

View File

@@ -58,7 +58,7 @@ impl Default for ProxyConfig {
max_history_lines: 100_000,
lookback_key: "[ctrl][6]".to_string(),
lookback_sequence: vec![0x1E],
auto_lookback_timeout_ms: 5000,
auto_lookback_timeout_ms: 15000,
}
}
}
@@ -100,6 +100,8 @@ pub struct Proxy {
vt_prev_screen: Option<vt100::Screen>,
last_output_time: Option<Instant>,
last_render_time: Option<Instant>,
last_stdin_time: Option<Instant>,
last_auto_lookback_time: Option<Instant>,
auto_lookback_timeout: Duration,
sync_buffer: Vec<u8>,
in_sync_block: bool,
@@ -182,6 +184,8 @@ impl Proxy {
vt_prev_screen: None,
last_output_time: None,
last_render_time: None,
last_stdin_time: None,
last_auto_lookback_time: None,
auto_lookback_timeout,
sync_buffer: Vec::with_capacity(SYNC_BUFFER_CAPACITY),
in_sync_block: false,
@@ -581,14 +585,38 @@ impl Proxy {
if self.in_lookback_mode || self.in_alternate_screen {
return Ok(());
}
// Check if enough time has passed since last stdin activity
let Some(stdin_time) = self.last_stdin_time else {
return Ok(());
};
if stdin_time.elapsed() < self.auto_lookback_timeout {
return Ok(());
}
// Check if there's been new output since last auto-lookback
// AND enough time has passed since we last dumped
let Some(render_time) = self.last_render_time else {
return Ok(());
};
if render_time.elapsed() < self.auto_lookback_timeout {
return Ok(());
if let Some(last_auto) = self.last_auto_lookback_time {
let no_new_output = render_time <= last_auto;
let too_soon = last_auto.elapsed() < self.auto_lookback_timeout;
if no_new_output || too_soon {
return Ok(());
}
}
debug!(
"auto_lookback triggered: stdin_idle={}ms render_age={}ms last_auto_age={}ms",
stdin_time.elapsed().as_millis(),
render_time.elapsed().as_millis(),
self.last_auto_lookback_time
.map(|t| t.elapsed().as_millis())
.unwrap_or(0)
);
self.dump_history(stdout_fd)?;
self.last_render_time = None;
self.last_auto_lookback_time = Some(Instant::now());
Ok(())
}
@@ -611,6 +639,8 @@ impl Proxy {
}
fn process_input<F: AsFd>(&mut self, data: &[u8], stdout_fd: &F) -> Result<()> {
self.last_stdin_time = Some(Instant::now());
if self.in_alternate_screen {
return write_all(&self.pty_master, data);
}