Skip to content

Environment Variable Support for Watch Mode Control #8459

@technicalpickles

Description

@technicalpickles

Clear and concise description of the problem

As a developer using coding agents (like Cursor, GitHub Copilot, VSCode extensions) with Vitest, I want to be able to run tests programmatically without the process hanging in watch mode, so that my agent workflows can complete successfully and provide immediate feedback.

Current Problem:
When coding agents run vitest in interactive terminal environments, the process defaults to watch mode (!isCI && process.stdin.isTTY = true), runs tests, then waits for file changes. This causes the agent to hang indefinitely, preventing it from receiving test results and continuing its workflow.

Current workarounds are suboptimal:

  • Use vitest run instead of vitest (requires agents to know about this command)
  • Set CI=true environment variable (misleading, not semantically correct)
  • Configure watch: false in vitest config (global override, not per-invocation)

This builds on the discussion in #7818 where @rhysburnie identified the same problem with AI agents getting stuck in watch mode. While PR #7673 restored the TTY check, it doesn't solve the core issue since coding agents still run in interactive terminals.

Suggested solution

Add environment variable support for explicit watch mode control:

New Environment Variables:

  • VITEST_WATCH=true - Force watch mode regardless of environment
  • VITEST_WATCH=false - Force run mode (no watch)
  • VITEST_RUN=true - Force run mode (alias for VITEST_WATCH=false)

Implementation:

// In packages/vitest/src/defaults.ts
function getWatchMode(): boolean {
  // Explicit overrides take precedence
  if (process.env.VITEST_WATCH === 'false' || process.env.VITEST_RUN === 'true') {
    return false
  }
  if (process.env.VITEST_WATCH === 'true') {
    return true
  }

  // Default detection (current behavior)
  return !isCI && process.stdin.isTTY
}

export const configDefaults = Object.freeze({
  // ... other defaults
  watch: getWatchMode(),
  // ... other defaults
})

Usage Examples:

# Force run mode for coding agents
VITEST_RUN=true vitest

# Force run mode (alternative)
VITEST_WATCH=false vitest

# Force watch mode (if needed)
VITEST_WATCH=true vitest

# Default behavior (unchanged)
vitest

Benefits:

  1. Backward Compatible - Existing developer workflows unchanged
  2. Explicit Control - Environmetn variables have clear intent for different scenarios, ie CI vs agents
  3. Agent-Friendly - Simple environment variable for agents to use
  4. Semantically Correct - VITEST_RUN=true is clearer than CI=true

Alternative

Alternative 1: Change default to vitest run

  • Pros: Solves the agent problem immediately
  • Cons: Breaking change that would affect all interactive development workflows

Alternative 2: Agent detection

  • Pros: Automatic detection of common agent environments
  • Cons: More complex, requires agents to set specific variables, may miss some agents

Alternative 3: Use existing CLI arguments

  • Pros: Explicit control via CLI flags (--run vs --run)
  • Cons: would need to do one of
    • projects update their package.json to have test vs test:watch
    • agents need to be trained (ie AGENTS.md or rules or whatever) to call vitest --run directly instead of using npm test

Alternative 4: Leave as is

Additional context

Additional context

Migration Guide:

For Users: No changes required - existing behavior is preserved.

For Coding Agents:

# Instead of: vitest
# Use: VITEST_RUN=true vitest

# Or in package.json scripts:
{
  "scripts": {
    "test": "VITEST_RUN=true vitest",
    "test:watch": "vitest"
  }
}

For CI/CD: No changes required - CI detection still works as before.

Testing Strategy:

test('VITEST_WATCH=false forces run mode', async () => {
  // Test that tests run and process exits
})

test('VITEST_RUN=true forces run mode', async () => {
  // Test that tests run and process exits
})

test('VITEST_WATCH=true forces watch mode', async () => {
  // Test that process enters watch mode
})

test('default behavior unchanged', async () => {
  // Test without any new env vars
})

Documentation Updates Needed:

  • docs/config/index.md - Document new environment variables
  • docs/guide/cli.md - Add examples for agent scenarios
  • docs/guide/features.md - Mention agent compatibility

This solution addresses the coding agent problem identified in #7818 while maintaining backward compatibility and providing a clear, explicit way to control watch mode behavior. It's a simple, safe addition that improves Vitest's usability in automated environments without affecting the excellent interactive development experience.

I intend to submit a PR for this issue if the approach is approved.

Validations

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Rejected

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions