From a2b376c26f2609d1ceb043090d29e0c781b68d75 Mon Sep 17 00:00:00 2001 From: June Kim Date: Sun, 10 May 2026 11:20:07 -0700 Subject: [PATCH] Fix file argument detection in non-TTY contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #398 where fx incorrectly treats file paths as code arguments when run in launch agents or other non-TTY contexts with stdin redirected to /dev/null. Root cause: The original code only checked isatty(stdin) to decide between file mode and pipe mode. In launch agents, stdin is not a TTY but also not a pipe (often /dev/null), causing fx to incorrectly assume piped input and treat the file path as JS code. Solution: Check if stdin is an actual input stream (pipe or regular file) vs a character device (/dev/null, /dev/tty). Use os.ModeCharDevice to distinguish: - Character devices (TTY, /dev/null) → file mode - Pipes and regular files → pipe mode This preserves all existing behavior while fixing the launch agent case. --- main.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 5855b7c4..42e97a6d 100644 --- a/main.go +++ b/main.go @@ -157,10 +157,19 @@ func main() { fd := os.Stdin.Fd() stdinIsTty := isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd) + // Check if stdin is actually piped/redirected input, not just non-TTY + // Default to false (treat as file/code mode) if we can't stat stdin + stdinIsInput := false + if stat, err := os.Stdin.Stat(); err == nil { + // ModeCharDevice is 0 for regular files and pipes, meaning actual data + stdinIsInput = (stat.Mode() & os.ModeCharDevice) == 0 + } + var fileName string var src io.Reader - if stdinIsTty { + // Use file mode when stdin is TTY or when stdin is a non-input device (e.g., /dev/null in launch agents) + if stdinIsTty || !stdinIsInput { if len(args) == 0 { // $ fx fmt.Println(usage())