Skip to content

Commit 95957dd

Browse files
committed
feat(tools): execute_bash uses PTY for shell integrations
- Create a PTY and spawn the command inside that - Reads from user input for text and key events, allowing some use for interactive commands - Uses current program env vars, and users custom shell integrations (dot files e.g. .zshrc) - Works for both `execute_bash` tool and context hooks - Moved PTY creation code from `figterm` to `fig_util` instead. NOTES: - terminal is set to dumb mode as explained in the code. we should find a way around this. - we provde all command output which is expensive for some interactive commands that re-draw. Our next steps should involve writing more than a max output to a local file, then prompting Q to read from this file if it wants more context about what the tool did. - Not tested on windows (testable?) or other shells besides zsh (yet)
1 parent 76ce78d commit 95957dd

File tree

15 files changed

+357
-175
lines changed

15 files changed

+357
-175
lines changed

Cargo.lock

Lines changed: 44 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/fig_util/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,24 @@ workspace = true
1414
default = []
1515

1616
[dependencies]
17+
anyhow.workspace = true
18+
shlex.workspace = true
19+
async-trait.workspace = true
1720
camino.workspace = true
1821
cfg-if.workspace = true
1922
clap.workspace = true
23+
crossterm.workspace = true
2024
dirs.workspace = true
2125
fig_os_shim.workspace = true
2226
hex.workspace = true
2327
indoc.workspace = true
2428
libc.workspace = true
2529
paste = "1.0.11"
30+
portable-pty.workspace = true
31+
termios = "0.3"
32+
nix = "0.26"
33+
filedescriptor = "0.8.3"
34+
console = "0.15.11"
2635
rand.workspace = true
2736
regex.workspace = true
2837
serde.workspace = true

crates/fig_util/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
pub mod consts;
12
pub mod directories;
3+
#[cfg(target_os = "macos")]
4+
pub mod launchd_plist;
25
pub mod manifest;
36
mod open;
47
pub mod process_info;
5-
mod shell;
8+
pub mod pty;
9+
pub mod shell;
610
pub mod system_info;
711
pub mod terminal;
812

9-
pub mod consts;
10-
#[cfg(target_os = "macos")]
11-
pub mod launchd_plist;
12-
1313
use std::cmp::Ordering;
1414
use std::path::{
1515
Path,
File renamed without changes.

crates/fig_util/src/terminal.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ use std::borrow::Cow;
22
use std::fmt;
33
use std::sync::OnceLock;
44

5+
use crossterm::event::{
6+
KeyCode,
7+
KeyEvent,
8+
KeyModifiers,
9+
};
510
use fig_os_shim::Context;
11+
use portable_pty::PtySize;
612
use serde::{
713
Deserialize,
814
Serialize,
915
};
10-
1116
/// Terminals that macOS supports
1217
pub const MACOS_TERMINALS: &[Terminal] = &[
1318
Terminal::Alacritty,
@@ -793,6 +798,58 @@ impl IntelliJVariant {
793798
}
794799
}
795800

801+
pub fn get_terminal_size() -> PtySize {
802+
match crossterm::terminal::size() {
803+
Ok((cols, rows)) => PtySize {
804+
rows,
805+
cols,
806+
pixel_width: 0,
807+
pixel_height: 0,
808+
},
809+
Err(_) => {
810+
// Fall back to default size
811+
PtySize {
812+
rows: 24,
813+
cols: 80,
814+
pixel_width: 0,
815+
pixel_height: 0,
816+
}
817+
},
818+
}
819+
}
820+
821+
pub fn key_event_to_bytes(key: KeyEvent) -> Vec<u8> {
822+
match key.code {
823+
KeyCode::Char(c) => {
824+
// Handle Ctrl+key combinations
825+
if key.modifiers.contains(KeyModifiers::CONTROL) {
826+
// Convert to control character (ASCII control chars are 1-26)
827+
if c.is_ascii_lowercase() {
828+
return vec![(c as u8) - b'a' + 1];
829+
} else if c.is_ascii_uppercase() {
830+
return vec![(c as u8) - b'A' + 1];
831+
}
832+
}
833+
// Regular character
834+
c.to_string().into_bytes()
835+
},
836+
KeyCode::Enter => vec![b'\r'],
837+
KeyCode::Backspace => vec![b'\x7f'],
838+
KeyCode::Esc => vec![b'\x1b'],
839+
KeyCode::Tab => vec![b'\t'],
840+
KeyCode::Up => vec![b'\x1b', b'[', b'A'],
841+
KeyCode::Down => vec![b'\x1b', b'[', b'B'],
842+
KeyCode::Right => vec![b'\x1b', b'[', b'C'],
843+
KeyCode::Left => vec![b'\x1b', b'[', b'D'],
844+
KeyCode::Home => vec![b'\x1b', b'[', b'H'],
845+
KeyCode::End => vec![b'\x1b', b'[', b'F'],
846+
KeyCode::Delete => vec![b'\x1b', b'[', b'3', b'~'],
847+
KeyCode::PageUp => vec![b'\x1b', b'[', b'5', b'~'],
848+
KeyCode::PageDown => vec![b'\x1b', b'[', b'6', b'~'],
849+
_ => vec![], // Ignore other keys for now
850+
}
851+
}
852+
796853
#[cfg(test)]
797854
mod tests {
798855
use std::sync::Arc;

0 commit comments

Comments
 (0)