fix(cli): remove problematic modifyOtherKeys terminal query

The '\x1b[>4;?m' query causes some terminals (like macOS Terminal.app) to echo a stray 'm' character on startup. This mode is redundant as we already support the modern Kitty keyboard protocol.
This commit is contained in:
Sehoon Shon
2026-02-06 20:46:08 -05:00
parent e844d4f45f
commit 2185aad332
2 changed files with 0 additions and 141 deletions

View File

@@ -7,10 +7,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { TerminalCapabilityManager } from './terminalCapabilityManager.js';
import { EventEmitter } from 'node:events';
import {
enableKittyKeyboardProtocol,
enableModifyOtherKeys,
} from '@google/gemini-cli-core';
// Mock fs
vi.mock('node:fs', () => ({
@@ -25,8 +21,6 @@ vi.mock('@google/gemini-cli-core', () => ({
},
enableKittyKeyboardProtocol: vi.fn(),
disableKittyKeyboardProtocol: vi.fn(),
enableModifyOtherKeys: vi.fn(),
disableModifyOtherKeys: vi.fn(),
enableBracketedPasteMode: vi.fn(),
disableBracketedPasteMode: vi.fn(),
}));
@@ -182,112 +176,4 @@ describe('TerminalCapabilityManager', () => {
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
});
describe('modifyOtherKeys detection', () => {
it('should detect modifyOtherKeys support (level 2)', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate modifyOtherKeys level 2 response: \x1b[>4;2m
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should not enable modifyOtherKeys for level 0', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate modifyOtherKeys level 0 response: \x1b[>4;0m
stdin.emit('data', Buffer.from('\x1b[>4;0m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
it('should prefer Kitty over modifyOtherKeys', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate both Kitty and modifyOtherKeys responses
stdin.emit('data', Buffer.from('\x1b[?1u'));
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(true);
expect(enableKittyKeyboardProtocol).toHaveBeenCalled();
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
it('should enable modifyOtherKeys when Kitty not supported', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate only modifyOtherKeys response (no Kitty)
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(false);
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should handle split modifyOtherKeys response chunks', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Split response: \x1b[>4;2m
stdin.emit('data', Buffer.from('\x1b[>4;'));
stdin.emit('data', Buffer.from('2m'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should detect modifyOtherKeys with other capabilities', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
stdin.emit('data', Buffer.from('\x1b]11;rgb:1a1a/1a1a/1a1a\x1b\\')); // background color
stdin.emit('data', Buffer.from('\x1bP>|tmux\x1b\\')); // Terminal name
stdin.emit('data', Buffer.from('\x1b[>4;2m')); // modifyOtherKeys
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.getTerminalBackgroundColor()).toBe('#1a1a1a');
expect(manager.getTerminalName()).toBe('tmux');
expect(enableModifyOtherKeys).toHaveBeenCalled();
});
it('should not enable modifyOtherKeys without explicit response', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate only DA1 response (no specific MOK or Kitty response)
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isKittyProtocolEnabled()).toBe(false);
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
});
});
});

View File

@@ -9,8 +9,6 @@ import {
debugLogger,
enableKittyKeyboardProtocol,
disableKittyKeyboardProtocol,
enableModifyOtherKeys,
disableModifyOtherKeys,
enableBracketedPasteMode,
disableBracketedPasteMode,
} from '@google/gemini-cli-core';
@@ -25,7 +23,6 @@ export class TerminalCapabilityManager {
private static readonly OSC_11_QUERY = '\x1b]11;?\x1b\\';
private static readonly TERMINAL_NAME_QUERY = '\x1b[>q';
private static readonly DEVICE_ATTRIBUTES_QUERY = '\x1b[c';
private static readonly MODIFY_OTHER_KEYS_QUERY = '\x1b[>4;?m';
// Kitty keyboard flags: CSI ? flags u
// eslint-disable-next-line no-control-regex
@@ -40,15 +37,11 @@ export class TerminalCapabilityManager {
static readonly OSC_11_REGEX =
// eslint-disable-next-line no-control-regex
/\x1b\]11;rgb:([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})(\x1b\\|\x07)?/;
// modifyOtherKeys response: CSI > 4 ; level m
// eslint-disable-next-line no-control-regex
private static readonly MODIFY_OTHER_KEYS_REGEX = /\x1b\[>4;(\d+)m/;
private detectionComplete = false;
private terminalBackgroundColor: TerminalBackgroundColor;
private kittySupported = false;
private kittyEnabled = false;
private modifyOtherKeysSupported = false;
private terminalName: string | undefined;
private constructor() {}
@@ -81,7 +74,6 @@ export class TerminalCapabilityManager {
// don't bother catching errors since if one write
// fails, the other probably will too
disableKittyKeyboardProtocol();
disableModifyOtherKeys();
disableBracketedPasteMode();
};
process.on('exit', cleanupOnExit);
@@ -99,7 +91,6 @@ export class TerminalCapabilityManager {
let terminalNameReceived = false;
let deviceAttributesReceived = false;
let bgReceived = false;
let modifyOtherKeysReceived = false;
// eslint-disable-next-line prefer-const
let timeoutId: NodeJS.Timeout;
@@ -149,21 +140,6 @@ export class TerminalCapabilityManager {
this.kittySupported = true;
}
// check for modifyOtherKeys support
if (!modifyOtherKeysReceived) {
const match = buffer.match(
TerminalCapabilityManager.MODIFY_OTHER_KEYS_REGEX,
);
if (match) {
modifyOtherKeysReceived = true;
const level = parseInt(match[1], 10);
this.modifyOtherKeysSupported = level >= 2;
debugLogger.log(
`Detected modifyOtherKeys support: ${this.modifyOtherKeysSupported} (level ${level})`,
);
}
}
// Check for Terminal Name/Version response.
if (!terminalNameReceived) {
const match = buffer.match(
@@ -199,7 +175,6 @@ export class TerminalCapabilityManager {
TerminalCapabilityManager.KITTY_QUERY +
TerminalCapabilityManager.OSC_11_QUERY +
TerminalCapabilityManager.TERMINAL_NAME_QUERY +
TerminalCapabilityManager.MODIFY_OTHER_KEYS_QUERY +
TerminalCapabilityManager.DEVICE_ATTRIBUTES_QUERY,
);
} catch (e) {
@@ -214,8 +189,6 @@ export class TerminalCapabilityManager {
if (this.kittySupported) {
enableKittyKeyboardProtocol();
this.kittyEnabled = true;
} else if (this.modifyOtherKeysSupported) {
enableModifyOtherKeys();
}
// Always enable bracketed paste since it'll be ignored if unsupported.
enableBracketedPasteMode();