fix: strip line/column suffixes from Windows path links

This PR implements a workaround for issue #26902 where terminal link handlers on Windows (specifically in the Antigravity editor and standard VS Code) fail to correctly parse and open absolute file paths that include `:line:column` suffixes. On Windows, colons are invalid characters in file paths (except for the drive letter), causing `FileSystemError` when these links are clicked.

### Changes:
- Added `stripLineColumnSuffixes` utility in `packages/core/src/utils/textUtils.ts`.
- Applied this utility across various output formatting paths to ensure safe link generation on Windows:
    - `packages/cli/src/ui/utils/textOutput.ts` (Non-interactive mode)
    - `packages/cli/src/ui/utils/markdownParsingUtils.ts` (Agent output in interactive mode)
    - `packages/core/src/utils/debugLogger.ts` (Core debug logging)
    - `packages/cli/src/ui/utils/ConsolePatcher.ts` (Interactive mode console redirection)
- Added comprehensive unit tests in `packages/core/src/utils/textUtils.test.ts`.

These changes are only active when running on Windows (`process.platform === 'win32'`).

Fixes #26902

cc @google-gemini/gemini-cli-maintainers
This commit is contained in:
gemini-cli[bot]
2026-05-14 15:57:19 +00:00
parent 6efdbd3e48
commit 2fdd259666
6 changed files with 109 additions and 10 deletions
+5 -1
View File
@@ -7,6 +7,7 @@
/* eslint-disable no-console */
import util from 'node:util';
import { stripLineColumnSuffixes } from '@google/gemini-cli-core';
import type { ConsoleMessageItem } from '../types.js';
interface ConsolePatcherParams {
@@ -45,7 +46,10 @@ export class ConsolePatcher {
console.info = this.originalConsoleInfo;
};
private formatArgs = (args: unknown[]): string => util.format(...args);
private formatArgs = (args: unknown[]): string => {
const formatted = util.format(...args);
return stripLineColumnSuffixes(formatted);
};
private patchConsoleMethod =
(type: 'log' | 'warn' | 'error' | 'debug' | 'info') =>
@@ -11,7 +11,7 @@ import {
INK_NAME_TO_HEX_MAP,
} from '../themes/color-utils.js';
import { theme } from '../semantic-colors.js';
import { debugLogger } from '@google/gemini-cli-core';
import { debugLogger, stripLineColumnSuffixes } from '@google/gemini-cli-core';
import { convertLatexToUnicode } from './latexToUnicode.js';
// Constants for Markdown parsing
@@ -108,6 +108,12 @@ export const parseMarkdownToANSI = (
defaultColor?: string,
): string => {
const baseColor = defaultColor ?? theme.text.primary;
// Strip line and column number suffixes from Windows paths BEFORE any other
// processing. This ensures that we don't break markdown parsing or URL
// detection, and that the final output is safe for Windows terminal links.
// See issue #26902.
const sanitizedRawText = stripLineColumnSuffixes(rawText);
// Convert LaTeX-style math/commands to Unicode BEFORE tokenizing markdown,
// so constructs like `$\{P_0, \dots, P_n\}$` are handled as a whole even
// when they contain underscores (which the tokenizer would otherwise treat
@@ -115,7 +121,7 @@ export const parseMarkdownToANSI = (
// conversion so their contents are preserved verbatim. Unknown `\foo`
// sequences are left alone, so Windows paths and regex escapes survive.
// See issue #25656.
const text = convertLatexPreservingSpans(rawText);
const text = convertLatexPreservingSpans(sanitizedRawText);
// Early return for plain text without markdown or URLs
if (!/[*_~`<[https?:]/.test(text)) {
return ansiColorize(text, baseColor);
+4 -2
View File
@@ -10,6 +10,7 @@
*/
import stripAnsi from 'strip-ansi';
import { stripLineColumnSuffixes } from '@google/gemini-cli-core';
export class TextOutput {
private atStartOfLine = true;
@@ -27,8 +28,9 @@ export class TextOutput {
if (str.length === 0) {
return;
}
this.outputStream.write(str);
const strippedStr = stripAnsi(str);
const processedStr = stripLineColumnSuffixes(str);
this.outputStream.write(processedStr);
const strippedStr = stripAnsi(processedStr);
if (strippedStr.length > 0) {
this.atStartOfLine = strippedStr.endsWith('\n');
}