Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/src/gemini.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
),
),
patchStdio: vi.fn(() => () => {}),
createInkStdio: vi.fn(() => ({
createWorkingStdio: vi.fn(() => ({
stdout: {
write: vi.fn((...args) =>
process.stdout.write(
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/gemini.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
recordSlowRender,
coreEvents,
CoreEvent,
createInkStdio,
createWorkingStdio,
patchStdio,
writeToStdout,
writeToStderr,
Expand Down Expand Up @@ -203,7 +203,7 @@ export async function startInteractiveUI(
consolePatcher.patch();
registerCleanup(consolePatcher.cleanup);

const { stdout: inkStdout, stderr: inkStderr } = createInkStdio();
const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio();

// Create wrapper component to use hooks inside render
const AppWrapper = () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/gemini_cleanup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
...actual,
writeToStdout: vi.fn(),
patchStdio: vi.fn(() => () => {}),
createInkStdio: vi.fn(() => ({
createWorkingStdio: vi.fn(() => ({
stdout: {
write: vi.fn(),
columns: 80,
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/nonInteractiveCli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
getMetrics: vi.fn(),
},
coreEvents: mockCoreEvents,
createWorkingStdio: vi.fn(() => ({
stdout: process.stdout,
stderr: process.stderr,
})),
};
});

Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/nonInteractiveCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
debugLogger,
coreEvents,
CoreEvent,
createWorkingStdio,
} from '@google/gemini-cli-core';

import type { Content, Part } from '@google/genai';
Expand Down Expand Up @@ -70,7 +71,8 @@ export async function runNonInteractive({
coreEvents.emitConsoleLog(msg.type, msg.content);
},
});
const textOutput = new TextOutput();
const { stdout: workingStdout } = createWorkingStdio();
const textOutput = new TextOutput(workingStdout);

const handleUserFeedback = (payload: UserFeedbackPayload) => {
const prefix = payload.severity.toUpperCase();
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/ui/AppContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
),
),
patchStdio: vi.fn(() => () => {}),
createInkStdio: vi.fn(() => ({
createWorkingStdio: vi.fn(() => ({
stdout: process.stdout,
stderr: process.stderr,
})),
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/ui/utils/textOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import stripAnsi from 'strip-ansi';

export class TextOutput {
private atStartOfLine = true;
private outputStream: NodeJS.WriteStream;

constructor(outputStream: NodeJS.WriteStream = process.stdout) {
this.outputStream = outputStream;
}

/**
* Writes a string to stdout.
Expand All @@ -22,7 +27,7 @@ export class TextOutput {
if (str.length === 0) {
return;
}
process.stdout.write(str);
this.outputStream.write(str);
const strippedStr = stripAnsi(str);
if (strippedStr.length > 0) {
this.atStartOfLine = strippedStr.endsWith('\n');
Expand Down
10 changes: 3 additions & 7 deletions packages/cli/src/zed-integration/zedIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
debugLogger,
ReadManyFilesTool,
getEffectiveModel,
createWorkingStdio,
startupProfiler,
} from '@google/gemini-cli-core';
import * as acp from './acp.js';
Expand All @@ -51,15 +52,10 @@ export async function runZedIntegration(
settings: LoadedSettings,
argv: CliArgs,
) {
const stdout = Writable.toWeb(process.stdout) as WritableStream;
const { stdout: workingStdout } = createWorkingStdio();
const stdout = Writable.toWeb(workingStdout) as WritableStream;
const stdin = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;

// Stdout is used to send messages to the client, so console.log/console.info
// messages to stderr so that they don't interfere with ACP.
console.log = console.error;
console.info = console.error;
console.debug = console.error;

new acp.AgentSideConnection(
(client: acp.Client) => new GeminiAgent(config, settings, argv, client),
stdout,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/code_assist/oauth2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ vi.mock('../utils/browser.js', () => ({
vi.mock('../utils/stdio.js', () => ({
writeToStdout: vi.fn(),
writeToStderr: vi.fn(),
createInkStdio: vi.fn(() => ({
createWorkingStdio: vi.fn(() => ({
stdout: process.stdout,
stderr: process.stderr,
})),
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/code_assist/oauth2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { FORCE_ENCRYPTED_FILE_ENV_VAR } from '../mcp/token-storage/index.js';
import { debugLogger } from '../utils/debugLogger.js';
import {
writeToStdout,
createInkStdio,
createWorkingStdio,
writeToStderr,
} from '../utils/stdio.js';
import {
Expand Down Expand Up @@ -334,7 +334,7 @@ async function authWithUserCode(client: OAuth2Client): Promise<boolean> {
const code = await new Promise<string>((resolve, _) => {
const rl = readline.createInterface({
input: process.stdin,
output: createInkStdio().stdout,
output: createWorkingStdio().stdout,
terminal: true,
});

Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/utils/stdio.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { patchStdio, createInkStdio } from './stdio.js';
import { patchStdio, createWorkingStdio } from './stdio.js';
import { coreEvents } from './events.js';

vi.mock('./events.js', () => ({
Expand Down Expand Up @@ -53,14 +53,14 @@ describe('stdio utils', () => {
expect(process.stderr.write).toBe(originalStderrWrite);
});

it('createInkStdio writes to real stdout/stderr bypassing patch', () => {
it('createWorkingStdio writes to real stdout/stderr bypassing patch', () => {
const cleanup = patchStdio();
const { stdout: inkStdout, stderr: inkStderr } = createInkStdio();
const { stdout, stderr } = createWorkingStdio();

inkStdout.write('ink stdout');
stdout.write('working stdout');
expect(coreEvents.emitOutput).not.toHaveBeenCalled();

inkStderr.write('ink stderr');
stderr.write('working stderr');
expect(coreEvents.emitOutput).not.toHaveBeenCalled();

cleanup();
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/stdio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ export function patchStdio(): () => void {
/**
* Creates proxies for process.stdout and process.stderr that use the real write methods
* (writeToStdout and writeToStderr) bypassing any monkey patching.
* This is used by Ink to render to the real output.
* This is used to write to the real output even when stdio is patched.
*/
export function createInkStdio() {
export function createWorkingStdio() {
const inkStdout = new Proxy(process.stdout, {
get(target, prop, receiver) {
if (prop === 'write') {
Expand Down