Don't prompt from git hooks when a TUI owns the terminal - #1907
Open
Soph wants to merge 1 commit into
Open
Conversation
Committing through lazygit froze on the "Link this commit to session context?" question the first time Entire ran in a repo. TUI git clients run `git commit` as a captured child while holding the controlling terminal in raw mode, so the hook inherits a /dev/tty that opens fine but must not be used: our write paints over their screen and our line read races their key reader for the keystroke, so the answer never arrives. CanPromptInteractively now also checks the mode of the /dev/tty it already opens — canonical input off means a full-screen program owns the screen, not a shell we can prompt. Suppressed prompts take the existing non-interactive path, so the hook auto-links the commit instead of blocking. lazygit sets no usable env var on its commit path, so there is no sentinel to key off; the mode check is also generic (gitui, tig, any TUI) and tracks what the TUIs themselves do, since they restore cooked mode whenever they deliberately hand the terminal to a child. Terminal mode rather than an isatty check on the hook's descriptors: git's hook stdio plumbing varies by version (stdin is /dev/null, and stdout/stderr may be inherited or captured), while terminal ownership does not. Detection fails open whenever the mode can't be read, so it can only ever suppress prompts we positively know are unusable. Measured with a probe hook on git 2.54: a human shell leaves the terminal canonical, a raw-mode parent does not; verified end to end that the predicate returns true under a cooked pty and false under a raw one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01KZ9JKKYRJZE534P7J9RN43RW
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a deadlock/hang scenario when entire is invoked from git hooks under terminal-UIs (e.g., lazygit) that keep the controlling TTY in raw mode. It extends interactive.CanPromptInteractively() to treat a raw-mode controlling terminal as non-interactive, forcing hooks down the existing non-interactive/autofallback paths.
Changes:
- Extend
/dev/ttydetection to also check terminal mode (ICANON) and suppress prompts when the controlling terminal is held in raw mode by a parent TUI. - Add Unix-specific ioctl implementations (
darwin/linux) with a fail-open fallback on unsupported platforms. - Add pty-backed unit test coverage (and a non-terminal “fail open” test), introducing
github.com/creack/ptyas a test dependency.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| go.mod | Adds github.com/creack/pty (used by new pty-based tests). |
| cmd/entire/cli/interactive/interactive.go | Updates CanPromptInteractively() to return false when /dev/tty is in raw mode. |
| cmd/entire/cli/interactive/rawmode_unix.go | Implements raw-mode detection via termios ioctl; fail-open on ioctl errors. |
| cmd/entire/cli/interactive/rawmode_linux.go | Defines Linux ioctl constant (TCGETS). |
| cmd/entire/cli/interactive/rawmode_darwin.go | Defines Darwin ioctl constant (TIOCGETA). |
| cmd/entire/cli/interactive/rawmode_other.go | Provides fail-open stub for non-darwin/non-linux platforms. |
| cmd/entire/cli/interactive/rawmode_test.go | Verifies fail-open behavior on non-terminal files. |
| cmd/entire/cli/interactive/rawmode_pty_test.go | Verifies canonical→raw→canonical transitions on a real pty. |
| CLAUDE.md | Updates documentation to reflect the new terminal-mode check in prompting precedence. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://entire.io/gh/entireio/cli/trails/981
Problem
A user reported that committing through lazygit froze on the interactive "Link this commit to session context?" question, hit the first time Entire ran in a new repo.
lazygit (like gitui, tig) runs
git commitas a captured child while holding the controlling terminal in raw mode for its own screen and keys. The hook inherits that terminal, so/dev/ttyopens fine — but it must not be used: our write paints over lazygit's UI, and our line read races lazygit's key reader for the keystroke, so the answer never arrives and the commit appears to hang.Fix
interactive.CanPromptInteractively()now also checks the mode of the/dev/ttyit already opens. Canonical input off (ICANON) means a full-screen program owns the screen, not a shell we can prompt. Suppressed prompts fall through to the existing non-interactive path, so the hook auto-links the commit rather than blocking.Why this signal:
GIT_OPTIONAL_LOCKS=0; its onlyGIT_TERMINAL_PROMPT=0(which we already detect) is on thereset/branchpaths, not commit. There is nothing lazygit-specific to key off./dev/null; stdout/stderr may be inherited or captured), while terminal ownership does not.Verification
Probe hook on git 2.54 (macOS), measuring what a real hook sees:
/dev/ttyopensgit commit -micanon echo-icanon -echotrueunder a cooked pty,falseunder a raw pty,falsewith no tty.ENTIRE_TEST_TTY=1is still checked first, so existing tests that force the interactive commit path are unaffected.golangci-lintclean.Windows has no
/dev/tty, so this prompt never fired there either way.Note for reviewers
github.com/creack/ptymoves from the module graph intogo.modas a direct (test-only) dependency for the pty test.One known follow-up, deliberately out of scope:
strategy/checkpoint_policy.goandhooks_git_cmd.gouse this same predicate to decide print-vs-log for one-shot stderr warnings, so those warnings now downgrade to log-only under a TUI. Swapping them to a writer-scoped check wouldn't help (lazygit pipes git's stderr, so that's false too); restoring visibility means printing unconditionally, aswarnIfAttributionDivergedalready does. That's a per-site design call better made on its own.🤖 Generated with Claude Code
Note
Low Risk
Narrow change to interactive detection with fail-open behavior; main effect is fewer prompts in TUI git contexts, with normal shell commits unchanged.
Overview
Fixes commits freezing when Entire tries to prompt (e.g. “Link this commit to session context?”) from a git hook while the user is in a TUI such as lazygit. Those clients keep the controlling terminal in raw mode;
/dev/ttystill opens, but prompting corrupts the UI and races for input.interactive.CanPromptInteractively()still follows the existing precedence (ENTIRE_TEST_TTY, tests, agent env, CI), but step 5 no longer stops at “/dev/ttyopens.” It reads the terminal termios and returns false when canonical input is off (ICANONclear), treating that as “a full-screen program owns the terminal.” Hooks then use the existing non-interactive path (e.g. auto-link) instead of blocking.Implementation is split across
ttyInRawModeon darwin/linux (TIOCGETA/TCGETS), fail-open on ioctl failure or non-Unix builds, plus pty-based tests and CLAUDE.md documentation.github.com/creack/ptyis added as a direct test dependency.Reviewed by Cursor Bugbot for commit 508c438. Configure here.