vitest-dev / vitest-dev/vitest

Agent detection disables colors in user code via shared tinyrainbow singleton

Open
#10,046 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
17.1k
Forks
2k
Avg merge
1d 22h
Merged PRs (30d)
94

Description

Describe the bug

Vitest 4.1.2 calls disableDefaultColors() from tinyrainbow 3.1.0 when std-env's isAgent is true (e.g. running inside Claude Code, Cursor, etc.):

// vitest/dist/chunks/cac.DRKYQDPl.js
import { isAgent } from 'std-env';
import { disableDefaultColors } from 'tinyrainbow';

if (isAgent) disableDefaultColors();

disableDefaultColors() mutates tinyrainbow's shared default export singleton via Object.assign(), turning all color functions into identity functions. This affects not just Vitest's own output, but also user code under test that imports tinyrainbow — because both Vitest and user code resolve to the same singleton instance.

This means any test that asserts on ANSI-colored output (e.g. testing a CLI formatter that uses tinyrainbow) silently breaks when run inside an AI agent environment.

Reproduction

  1. Create a module that uses tinyrainbow:
import color from "tinyrainbow";
export const warn = (msg: string) => `${color.yellow("⚠")} ${msg}`;
  1. Write a test asserting colored output:
it("should produce yellow warning symbol", () => {
  expect(warn("oops")).toContain("\x1b[33m⚠\x1b[39m");
});
  1. Run with CLAUDECODE=1 (or any env that makes std-env detect an agent):
CLAUDECODE=1 vitest run

The test fails because color.yellow becomes an identity function.

Expected behavior: Vitest's agent detection should only affect Vitest's own reporter output, not mutate shared state that user code depends on.

Possible solutions:

  • Use createColors() for Vitest's internal rendering instead of mutating the default singleton
  • Scope the disable to Vitest's reporter layer rather than the global default

System Info

  System:
    OS: Windows 11 10.0.26100
    CPU: (32) x64 AMD Ryzen 9 9950X3D 16-Core Processor
    Memory: 37.30 GB / 93.64 GB
  Binaries:
    Node: 25.8.2 - C:\Users\Christopher\.local\share\mise\installs\node\25.8.2\node.EXE
    npm: 11.11.1 - C:\Users\Christopher\.local\share\mise\installs\node\25.8.2\npm.CMD
    pnpm: 10.33.0 - C:\Users\Christopher\.local\share\mise\installs\npm-corepack\0.34.6\pnpm.CMD
    bun: 1.3.11 - C:\Users\Christopher\.local\share\mise\installs\bun\1.3.11\bin\bun.EXE
    Deno: 2.7.10 - C:\Users\Christopher\.local\share\mise\installs\deno\2.7.10\bin\deno.EXE
  Browsers:
    Edge: Chromium (140.0.3485.54)
    Firefox: 149.0 - C:\Program Files\Mozilla Firefox\firefox.exe
  npmPackages:
    @vitest/coverage-v8: catalog:test => 4.1.2
    @vitest/eslint-plugin: catalog:lint => 1.6.13
    vitest: catalog:test => 4.1.2

Used Package Manager

pnpm

Validations


Generated with Claude Code
Verified manually by me!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the agent check and disableDefaultColors() call in vitest/dist/chunks/cac.DRKYQDPl.js, then inspect how Vitest reporter rendering obtains its colors. Reproduce with CLAUDECODE=1 and a user module importing tinyrainbow. Done means Vitest output can disable its own colors without changing the colored output asserted by user code.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.