fix(core): improve headless mode detection for flags and query args (#18855)

This commit is contained in:
Gal Zahavi
2026-02-11 16:20:54 -08:00
committed by GitHub
parent 941691ce72
commit 08e8eeab84
5 changed files with 94 additions and 25 deletions

View File

@@ -99,16 +99,50 @@ describe('isHeadlessMode', () => {
expect(isHeadlessMode({ prompt: true })).toBe(true);
});
it('should return false if query is provided but it is still a TTY', () => {
// Note: per current logic, query alone doesn't force headless if TTY
// This matches the existing behavior in packages/cli/src/config/config.ts
expect(isHeadlessMode({ query: 'test query' })).toBe(false);
it('should return true if query is provided', () => {
expect(isHeadlessMode({ query: 'test query' })).toBe(true);
});
it('should return true if -p or --prompt is in process.argv as a fallback', () => {
const originalArgv = process.argv;
process.argv = ['node', 'index.js', '-p', 'hello'];
try {
expect(isHeadlessMode()).toBe(true);
} finally {
process.argv = originalArgv;
}
process.argv = ['node', 'index.js', '--prompt', 'hello'];
try {
expect(isHeadlessMode()).toBe(true);
} finally {
process.argv = originalArgv;
}
});
it('should return true if -y or --yolo is in process.argv as a fallback', () => {
const originalArgv = process.argv;
process.argv = ['node', 'index.js', '-y'];
try {
expect(isHeadlessMode()).toBe(true);
} finally {
process.argv = originalArgv;
}
process.argv = ['node', 'index.js', '--yolo'];
try {
expect(isHeadlessMode()).toBe(true);
} finally {
process.argv = originalArgv;
}
});
it('should handle undefined process.stdout gracefully', () => {
const originalStdout = process.stdout;
// @ts-expect-error - testing edge case
delete process.stdout;
Object.defineProperty(process, 'stdout', {
value: undefined,
configurable: true,
});
try {
expect(isHeadlessMode()).toBe(false);
@@ -122,8 +156,10 @@ describe('isHeadlessMode', () => {
it('should handle undefined process.stdin gracefully', () => {
const originalStdin = process.stdin;
// @ts-expect-error - testing edge case
delete process.stdin;
Object.defineProperty(process, 'stdin', {
value: undefined,
configurable: true,
});
try {
expect(isHeadlessMode()).toBe(false);

View File

@@ -28,18 +28,25 @@ export interface HeadlessModeOptions {
* @returns true if the environment is considered headless.
*/
export function isHeadlessMode(options?: HeadlessModeOptions): boolean {
if (process.env['GEMINI_CLI_INTEGRATION_TEST'] === 'true') {
return (
!!options?.prompt ||
(!!process.stdin && !process.stdin.isTTY) ||
(!!process.stdout && !process.stdout.isTTY)
);
if (process.env['GEMINI_CLI_INTEGRATION_TEST'] !== 'true') {
const isCI =
process.env['CI'] === 'true' || process.env['GITHUB_ACTIONS'] === 'true';
if (isCI) {
return true;
}
}
return (
process.env['CI'] === 'true' ||
process.env['GITHUB_ACTIONS'] === 'true' ||
!!options?.prompt ||
const isNotTTY =
(!!process.stdin && !process.stdin.isTTY) ||
(!!process.stdout && !process.stdout.isTTY)
(!!process.stdout && !process.stdout.isTTY);
if (isNotTTY || !!options?.prompt || !!options?.query) {
return true;
}
// Fallback: check process.argv for flags that imply headless or auto-approve mode.
return process.argv.some(
(arg) =>
arg === '-p' || arg === '--prompt' || arg === '-y' || arg === '--yolo',
);
}