Skip to content

strip_ansi and ansi_filter do not strip OSC sequences (OSC-8 hyperlinks survive) #13

Description

@osazemeu

Both ANSI strippers only handle CSI sequences:

src/utils.rs:48

pub fn strip_ansi(text: &str) -> String {
    lazy_static::lazy_static! {
        static ref ANSI_RE: Regex = Regex::new(r"\x1b\[[0-9;]*[a-zA-Z]").unwrap();
    }
    ANSI_RE.replace_all(text, "").to_string()
}

src/ansi_filter.rs:13 has the same pattern.

OSC sequences use a different introducer (\x1b]) and a different terminator (BEL or ST), so they pass through untouched.

Where it shows

Yarn Berry wraps every message code except YN0000 in an OSC-8 hyperlink when it has a terminal:

\x1b]8;;https://yarnpkg.com/advanced/error-codes#yn0060---incompatible_peer_dependency\x07YN0060\x1b]8;;\x07: | react is listed by...

After CSI-only stripping that renders as inline garbage:

➤ ]8;;https://yarnpkg.com/advanced/error-codes#yn0060---incompatible_peer_dependencyYN0060]8;;: | react is listed by...

which both wastes tokens and breaks any pattern anchored on ^➤ YN\d{4}: . Other tools use OSC-8 too, so this is not yarn-specific.

Suggested fix

Strip OSC alongside CSI:

static ref OSC_RE: Regex = Regex::new(r"\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)").unwrap();

Scope note

Piped output carries no OSC-8, and ContextZip captures piped output, so this only bites when something hands the child process a terminal. That makes it low urgency but not zero, since CI wrappers and hooks do sometimes leave stderr attached while capturing stdout.

Found while adding yarn and bun support in #11.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions