fix(review): honor agent prompt sentinels · Entire
fix(review): honor agent prompt sentinels
dfd203c→main·
dipree·3d ago·2 files·+19 added/-15 removed
Sessions
01KXGT7DJ8E7EC047SBCZXN1Z3View transcript
[?
Fix Review Interactive Setup and DefaultsPi·GPT-5.6-sol·1 step](/content/gh/entireio/cli/session/019f60f0-7ee7-73b0-b787-4c8cbc646802#timeline-01KXGT7DJ8E7EC047SBCZXN1Z3/index.html)
Changes
2
cmd/entire/cli/review
Mcmd.go+10/-8
Mconfigure_test.go+9/-7
269 unmodified lines
270
271
272
273
274
275
276
277
278
273
274
275
276
277
278
279
3 unmodified lines
283
284
285
286
287
288
289
8 unmodified lines
298
299
300
302
303
301
302
303
304
305
306
307
308
269 unmodified lines
Slots []string // reviewer slots as "agent[=model]" entries (--set-slot)
}
// reviewCommandIsInteractive treats a real terminal on both stdin and stdout
// as authoritative for this explicitly user-invoked command. This avoids
// suppressing the review wizard when a normal shell inherits an agent sentinel
// or GIT_TERMINAL_PROMPT=0, while requiring the exact stdin consumed by huh and
// Bubble Tea to accept keypresses. A controlling /dev/tty alone is insufficient
// because the command may still have piped stdin.
// reviewCommandIsInteractive requires the exact stdin consumed by huh and
// Bubble Tea, plus stdout, to be terminals. CanPromptInteractively adds the
// independent policy gate for tests, CI, and agent subprocess sentinels; a
// controlling /dev/tty alone is insufficient because stdin may still be piped.
func reviewCommandIsInteractive(cmd *cobra.Command) bool {
hardDisabled := reviewInteractivityHardDisabled(
os.Getenv(interactive.EnvTestTTY),
3 unmodified lines
return reviewTTYIsInteractive(
interactive.IsTerminalReader(cmd.InOrStdin()),
interactive.IsTerminalWriter(cmd.OutOrStdout()),
interactive.CanPromptInteractively(),
hardDisabled,
)
}
8 unmodified lines
return underTest || (ci != "" && ci != "false")
}
func reviewTTYIsInteractive(stdinTTY, stdoutTTY, hardDisabled bool) bool {
return !hardDisabled && stdinTTY && stdoutTTY
func reviewTTYIsInteractive(stdinTTY, stdoutTTY, canPrompt, hardDisabled bool) bool {
// Real stdio terminals are necessary but not sufficient: agent shells can
// allocate a PTY while advertising that no human is available through the
// sentinels enforced by CanPromptInteractively.
return !hardDisabled && stdinTTY && stdoutTTY && canPrompt
}
func (o reviewConfigureOptions) scripted() bool {
Mcmd/entire/cli/review/cmd.go+10/-8
121 unmodified lines
122
123
124
125
126
127
128
128
129
130
131
132
129
130
131
132
133
134
135
136
137
138
137
138
139
140
141
142
143
121 unmodified lines
name string
stdinTTY bool
stdoutTTY bool
canPrompt bool
hardDisabled bool
want bool
}{
{name: "direct terminal overrides inherited sentinel", stdinTTY: true, stdoutTTY: true, want: true},
{name: "controlling terminal does not override piped stdin", stdinTTY: false, stdoutTTY: true, want: false},
{name: "captured stdout", stdinTTY: true, stdoutTTY: false, want: false},
{name: "agent with piped stdin", stdinTTY: false, stdoutTTY: true, want: false},
{name: "explicitly forced non-interactive", stdinTTY: true, stdoutTTY: true, hardDisabled: true, want: false},
{name: "direct human terminal", stdinTTY: true, stdoutTTY: true, canPrompt: true, want: true},
{name: "agent sentinel overrides real PTY", stdinTTY: true, stdoutTTY: true, canPrompt: false, want: false},
{name: "controlling terminal does not override piped stdin", stdinTTY: false, stdoutTTY: true, canPrompt: true, want: false},
{name: "captured stdout", stdinTTY: true, stdoutTTY: false, canPrompt: true, want: false},
{name: "agent with piped stdin", stdinTTY: false, stdoutTTY: true, canPrompt: false, want: false},
{name: "explicitly forced non-interactive", stdinTTY: true, stdoutTTY: true, canPrompt: true, hardDisabled: true, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := reviewTTYIsInteractive(tt.stdinTTY, tt.stdoutTTY, tt.hardDisabled); got != tt.want {
t.Fatalf("reviewTTYIsInteractive(%v, %v, %v) = %v, want %v", tt.stdinTTY, tt.stdoutTTY, tt.hardDisabled, got, tt.want)
if got := reviewTTYIsInteractive(tt.stdinTTY, tt.stdoutTTY, tt.canPrompt, tt.hardDisabled); got != tt.want {
t.Fatalf("reviewTTYIsInteractive(%v, %v, %v, %v) = %v, want %v", tt.stdinTTY, tt.stdoutTTY, tt.canPrompt, tt.hardDisabled, got, tt.want)
}
}
}
}`