This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
NEVER use vizzly-co as the GitHub organization. It is ALWAYS vizzly-testing.
The product URL is vizzly.dev (NOT vizzly.com or any other domain).
Vizzly is a visual review platform for UI developers and designers. Unlike tools that render components in isolation, Vizzly captures screenshots directly from your functional tests—the real thing. It enables teams to collaborate on visual changes through an intuitive web dashboard.
This CLI is the core of Vizzly. It provides:
- Client SDK - Lightweight API for test runners to send screenshots
- TDD Mode - Local visual testing with interactive dashboard, settings, and project tools
- Cloud Integration - Upload screenshots to Vizzly's web platform for team review
- CI/CD Support - Run tests in parallel, wait for results, handle visual regressions
- Plugin System - Extensible architecture for adding custom commands and integrations
TDD Mode (Local Development):
- Visual test-driven development workflow
- No API token required for visual testing
- Screenshots compared locally using
honeydiff(high-performance Rust-based diffing) - Interactive dashboard at
http://localhost:47392- View and accept/reject baselines
- Settings tab for editing configuration without touching files
- Projects tab for authentication and project mappings
- Fast iteration cycle
- Command:
vizzly tdd startorvizzly tdd run "npm test"
Run Mode (CI/CD & Cloud):
- Requires API token
- Screenshots uploaded to Vizzly cloud
- Team reviews changes via web dashboard
- Supports parallel test execution across shards
- Returns exit codes for CI integration
- Command:
vizzly run "npm test" --wait
npm run build # Full build: clean, compile, copy assets, reporter, types
npm run clean # Remove dist directory
npm run compile # Babel compile src to dist
npm run types # Generate TypeScript declarations
npm test # Run all tests once (node --test)
npm test:watch # Run tests in watch mode
npm run test:reporter # Run Playwright visual regression tests for reporter UI
npm run test:reporter:visual # Self-test: run reporter tests with Vizzly
npm run lint # ESLint check
npm run lint:fix # ESLint auto-fix
npm run format # Prettier format
npm run format:check # Prettier checknode --test tests/commands/builds.test.js # Single test file
node --test $(find tests/services -name '*.test.js') # Directorynpm run dev:reporter # Start Vite dev server for reporter UI (port 5173)
npm run build:reporter # Build reporter production bundleService-Oriented Design:
The CLI uses a dependency injection container (src/container/) to wire up services with proper
lifecycle management. Services are singletons registered with dependencies, making it easy to test
and compose functionality.
Client SDK Philosophy:
The client SDK (@vizzly-testing/cli/client) is intentionally thin—it just POSTs screenshot data to
a local HTTP server. This keeps test runner integration simple and language-agnostic. The heavy
lifting happens server-side.
Reporter UI:
The interactive dashboard is a React SPA (src/reporter/) built with Vite. It serves two purposes:
- Live TDD dashboard fetching data from HTTP endpoints
- Static HTML reports with embedded JSON data
Same codebase, different data sources.
Plugin Architecture:
The CLI supports a plugin system (src/plugin-loader.js) that enables extensibility while keeping the
core lean. Plugins are ESM modules that register Commander.js commands and receive access to the
service container, config, and output utilities.
- Auto-discovery: Plugins under
@vizzly-testing/*scope are automatically discovered from node_modules - Config-based: Plugins can be explicitly loaded via
vizzly.config.js - Security: Path validation prevents directory traversal, scope restriction limits auto-discovery
- Graceful errors: Plugin failures don't crash the CLI
- Timeout protection: 5-second timeout prevents infinite loops during registration
- Deduplication: Same plugin won't load twice (warns on version conflicts)
TDD Workflow (Local Development):
- Developer runs
vizzly tdd start→ Background TDD server starts - Developer runs tests in watch mode →
vizzlyScreenshot()sends images to server - Server compares using
honeydiff→ Saves results to.vizzly/ - Dashboard at
http://localhost:47392shows:- Live comparisons → Accept/reject baselines
- Settings tab → Edit config (threshold, ports, etc.) without touching files
- Projects tab → Login, manage project mappings as convenient
- Baselines updated → Tests pass on next run
One-off TDD Run:
- Developer runs
vizzly tdd run "npm test"→ Ephemeral server starts - Tests execute once → Screenshots captured and compared
- Static HTML report generated → Opens in browser
- Server automatically stops
CI/CD Workflow:
- CI runs
vizzly run "npm test" --wait→ Starts screenshot server - Tests run → Client SDK sends screenshots to server
- Server queues screenshots → Batch uploads to cloud after tests
--waitpolls build status → Returns exit code based on visual differences- Team reviews on web dashboard → Approves/requests changes
Parallel Builds:
- Multiple CI shards run with same
--parallel-id - Each shard uploads its screenshots independently
- After all shards complete, run
vizzly finalize <parallel-id> - Cloud processes complete build and shows unified results
Priority (highest to lowest):
- CLI flags (
--port,--threshold, etc.) - Environment variables (
VIZZLY_TOKEN,VIZZLY_API_URL) - Config file (
vizzly.config.jsfound via cosmiconfig) - Built-in defaults
Config files use defineConfig() helper for better IDE support.
Screenshot Identity:
Screenshots are matched by signature: name|viewport_width|browser. This ensures the same logical
screenshot across different runs can be compared, even if viewport or browser differs.
Baseline Management:
- TDD mode: Baselines stored in
.vizzly/baselines/as PNG files - Cloud mode: Baselines are previous build's screenshots on same branch
- Both use SHA-based deduplication to avoid redundant uploads/storage
Auto-Discovery:
The client SDK automatically discovers a running TDD server by searching for .vizzly/server.json
in parent directories. This allows tests to work without explicit configuration.
Dogfooding:
The reporter UI's visual tests (tests/reporter/) use Vizzly itself for visual regression testing.
Static vs Live Reports: The React-based reporter works in two modes:
- Live mode: TDD server serves dashboard that polls for updates (live comparisons, settings, projects)
- Static mode: Self-contained HTML report with embedded data (comparisons only, generated by
vizzly tdd run)
VIZZLY_HOME- Override config directory (default: ~/.vizzly). Useful for development/testingVIZZLY_TOKEN- API authentication tokenVIZZLY_API_URL- API base URL (default: https://app.vizzly.dev)VIZZLY_LOG_LEVEL- Logging level (debug|info|warn|error)VIZZLY_BUILD_NAME- Custom build name (useful in CI for dynamic naming)VIZZLY_PARALLEL_ID- Parallel build identifierVIZZLY_ENABLED- Enable/disable SDK (default: auto-detect)VIZZLY_SERVER_URL- Screenshot server URL for clientVIZZLY_BUILD_ID- Build identifier for grouping screenshots
Git information overrides (for CI):
VIZZLY_COMMIT_SHAVIZZLY_COMMIT_MESSAGEVIZZLY_BRANCHVIZZLY_PR_NUMBER
The package provides multiple entry points for different use cases:
@vizzly-testing/cli- Main exports (SDK, client, utilities)@vizzly-testing/cli/client- Lightweight test runner integration (vizzlyScreenshot)@vizzly-testing/cli/sdk- Full SDK with programmatic upload control@vizzly-testing/cli/config- Config helpers (defineConfig)
- Node test runner for unit/integration tests (
npm test) - Playwright for reporter UI visual tests (
npm run test:reporter) - Coverage thresholds: 75% lines/functions, 70% branches
- Tests mirror
src/structure intests/
Adding a new command:
- Create command file in
src/commands/ - Register in
src/cli.jswith Commander - Use
loadConfig()to merge CLI options with config file - Add tests in
tests/commands/
Adding a new service:
- Create a plain class in
src/services/ - Add it to
createServices()insrc/services/index.jswith its dependencies - Access via
services.myServicein commands
Modifying screenshot comparison: Remember to update both local (TDD) and cloud comparison logic to keep behavior consistent.
Creating a plugin:
- Create ESM module that exports
{ name, version?, register(program, context) } - Plugin receives
program(Commander instance),config,output, andservicesobject - For official plugins: Publish under
@vizzly-testing/*scope withvizzly.pluginfield in package.json - For community/local plugins: Add path to
pluginsarray invizzly.config.js - Add tests in plugin's own package or in
tests/integration/ - See
docs/plugins.mdandexamples/custom-plugin/for complete guide
When developing the CLI, use VIZZLY_HOME to isolate dev state from production:
# Set up isolated dev environment
export VIZZLY_HOME="$HOME/.vizzly.dev"
export VIZZLY_API_URL="http://localhost:3000"
# Now login, link projects, etc. - all stored in ~/.vizzly.dev
vizzly login
vizzly project linkThis keeps your production auth/projects in ~/.vizzly untouched while you work on the CLI.
Recommended: Add a .envrc file (with direnv) to auto-set these when entering the repo.
NEVER add AI attribution to commits, PRs, or any writing:
- ❌ "🤖 Generated with Claude Code"
- ❌ "Co-Authored-By: Claude noreply@anthropic.com"
Commits:
- Use gitmoji prefix (✨ feat, 🐛 fix, 📝 docs, ♻️ refactor, 🧪 test, etc.)
- Keep messages clear and concise
Pull Requests:
- Title must include gitmoji prefix matching the primary change type
- Description must explain the why (motivation/problem being solved)
- Description must cover all changes in the diff, not just highlights
- Use a "Summary" section with bullet points for each change
- Include a "Test plan" section with verification steps