Complete reference for Meticulous CLI commands, flags, and usage patterns.
Install the CLI globally or use npx:
# Using npx (recommended)
npx @alwaysmeticulous/cli [command]
# Or install globally
npm install -g @alwaysmeticulous/cli
meticulous [command]
| Command | Purpose | Use Case |
|---|---|---|
ci run-with-tunnel | Run tests in cloud via tunnel | CI testing with local app |
ci upload-assets | Upload and test static assets | CI testing for static sites |
ci upload-asset-chunk | Upload one named, versioned asset chunk | Multi-bundle deployments |
ci run-with-uploaded-asset-chunks | Trigger a test run against uploaded chunks | Multi-bundle deployments |
ci upload-container | Upload Docker container and test | CI testing with containers |
ci run-local | Run all replay test cases locally | Local test execution |
ci prepare | Ensure base run exists | CI setup |
ci start-tunnel | Start secure tunnel | Manual testing/debugging |
simulate (alias: replay) | Replay session locally | Local debugging |
record session | Record a user session | Session recording |
record login | Record a login flow | Login flow recording |
auth login | Force a fresh browser login and select a project | Authentication |
auth whoami | Show current user | Authentication check |
auth logout | Clear stored tokens | Authentication |
auth set-project | Select the project to use with OAuth | Authentication |
auth unset-project | Clear the selected project | Authentication |
auth list-projects | List the projects you can access | Authentication |
project show | Show linked project | Project info |
project upload-source | Upload a source-code archive for a given commit | Source coverage / CI |
download session | Download a recorded session | Debugging |
download replay | Download a replay | Debugging |
download test-run | Download a test run | Debugging |
local relevant-sessions | Find sessions covering the current branch's code changes | Local development |
debug replay | Set up a debug workspace for a single replay | Investigating a replay |
debug replay-diff | Set up a debug workspace for a specific replay diff | Investigating a diff |
debug clean | Clean up debug workspaces | Debug workspace maintenance |
agent upload-build | Upload a build (static assets or container) and capture a deployment ID | Agent/programmatic use |
agent trigger-test-run | Trigger a test run against an uploaded build | Agent/programmatic use |
agent test-run-diffs | List replay diffs for a test run with summary | Agent/programmatic use |
agent dom-diff | Get the DOM diff for a replay-diff screenshot | Agent/programmatic use |
agent image-urls | Get screenshot image URLs for a replay-diff screenshot | Agent/programmatic use |
agent image-files | Download screenshot images to ~/.meticulous/agent-images | Agent/programmatic use |
agent timeline-diff | Get the timeline diff for a replay diff | Agent/programmatic use |
agent test-run-for-commit | Look up the latest test run for a commit (defaults to git HEAD) | Agent/programmatic use |
agent js-coverage | Get JS coverage for a replay or a whole test run | Agent/programmatic use |
agent js-coverage-diff | Get the JS coverage diff (base vs head) for a replay diff | Agent/programmatic use |
schema | Print the CLI command schema as JSON | Agent/programmatic use |
For a closer look at the agent and auth commands — including their flags and how they compose into agent workflows — see CLI commands for agents.
Run Meticulous tests in the cloud against a locally-running application.
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="<token>" \
--appUrl="<url>" \
[options]
--apiToken
Type: String Description: Your Meticulous API token How to get: From Meticulous dashboard project settings
Example:
--apiToken="met_live_abc123..."
Note: Can also be set via METICULOUS_API_TOKEN environment variable.
--appUrl
Type: String Description: URL where your app is running Format: Full URL including protocol and port
Examples:
--appUrl="http://localhost:3000"
--appUrl="http://localhost:8080"
--appUrl="https://localhost:3000"
--commitSha
Type: String Description: Commit SHA being tested Default: Auto-detected from git
Example:
--commitSha="$GITHUB_SHA"
--commitSha="abc123def456..."
--companionAssetsFolder
Type: String (path) Description: Path to local folder with static assets to upload Default: None Requires: Must also provide --companionAssetsRegex
Example:
--companionAssetsFolder="companion-assets"
--companionAssetsRegex
Type: String (regex) Description: Regex pattern for requests to serve from companion assets Default: None Requires: Must also provide --companionAssetsFolder
Example:
--companionAssetsRegex="^/_next/static/"
--proxyAllUrls
Type: Boolean Description: Proxy all URLs through tunnel (not just app URL) Default: false
Example:
--proxyAllUrls
Use case: Multi-server applications (frontend + API on different ports)
--rewriteHostnameToAppUrl
Type: Boolean Description: Rewrite request hostname to match app URL Default: false
Example:
--rewriteHostnameToAppUrl
Use case: When HTML contains absolute URLs
--secureTunnelHost
Type: String Description: Custom tunnel server host Default: Meticulous production tunnel Note: For Meticulous team use only
--hadPreparedForTests
Type: Boolean Description: Indicate that meticulous ci prepare was already run Default: false
# Basic usage
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="$METICULOUS_API_TOKEN" \
--appUrl="http://localhost:3000"
# With companion assets (Next.js)
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="$METICULOUS_API_TOKEN" \
--appUrl="http://localhost:3000" \
--companionAssetsFolder="companion-assets" \
--companionAssetsRegex="^/_next/static/"
# Multi-server app
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="$METICULOUS_API_TOKEN" \
--appUrl="http://localhost:3000" \
--proxyAllUrls
| Code | Meaning |
|---|---|
| 0 | Success - all tests passed or diffs approved |
| 1 | Failure - tests failed or unapproved diffs |
| 2 | Error - configuration or connection error |
Upload static assets and run tests in the cloud.
npx @alwaysmeticulous/cli ci upload-assets \
--apiToken="<token>" \
--appDirectory="<path>" \
[options]
--apiToken
Type: String Description: Your Meticulous API token
--appDirectory
Type: String (path) Description: Path to directory containing built static assets Common values: dist, build, out
Examples:
--appDirectory="dist" # Vite
--appDirectory="build" # Create React App
--appDirectory="out" # Next.js static export
--commitSha
Type: String Description: Commit SHA being tested Default: Auto-detected from git
--rewrites
Type: String (JSON) Description: URL rewrite rules in Vercel format Use case: SPA routing, redirects
Example:
--rewrites='[{"source":"/(.*)", "destination":"/index.html"}]'
Common patterns:
SPA routing:
[{"source": "/(.*)", "destination": "/index.html"}]
API proxy:
[{"source": "/api/(.*)", "destination": "https://api.example.com/$1"}]
--waitForBase
Type: Boolean Description: Wait for base test run Default: false
--waitForTestRunToComplete
Type: Boolean Description: After the upload succeeds and Meticulous has started a test run, keep polling until that run reaches a terminal status, then exit non-zero if the run failed.
Default: false (omit the flag)
Standard CI setups should leave this flag off. For GitHub, GitLab, Buildkite, CircleCI, and similar pipelines, the usual pattern is: build, run ci upload-assets (or ci upload-container) to upload and trigger a run, then let the job exit. Meticulous reports progress and outcomes on the pull request or through your VCS integration. Holding the whole CI job open until every replay finishes makes pipelines slower, rarely adds value, and tooling (including some AI code reviewers) often suggests this flag because the name sounds helpful.
Why avoid it by default: the run can still have background work in some configurations (for example lazy session execution), so a naive "wait until complete" loop may exit too early or fail with errors that are hard to interpret if you were only trying to "wait for Meticulous."
When it may be appropriate: automation that deliberately must block until the run is fully finished (for example internal regression checks where the process relies on the CLI exit code). If you are onboarding a new project or wiring PR checks, you almost never need this flag.
# Basic usage
npx @alwaysmeticulous/cli ci upload-assets \
--apiToken="$METICULOUS_API_TOKEN" \
--appDirectory="dist"
# With SPA routing
npx @alwaysmeticulous/cli ci upload-assets \
--apiToken="$METICULOUS_API_TOKEN" \
--appDirectory="dist" \
--rewrites='[{"source":"/(.*)", "destination":"/index.html"}]'
# With commit SHA
npx @alwaysmeticulous/cli ci upload-assets \
--apiToken="$METICULOUS_API_TOKEN" \
--appDirectory="build" \
--commitSha="$CI_COMMIT_SHA"
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Failure |
| 2 | Error |
Upload a named, versioned chunk of static assets to Meticulous for incremental deployments.
npx @alwaysmeticulous/cli ci upload-asset-chunk \
--apiToken="<token>" \
--chunkName="<name>" \
--chunkVersionId="<version>" \
--chunkAssetsDirectory="<path>" \
[options]
--apiToken
Type: String Description: Your Meticulous API token
Note: Can also be set via METICULOUS_API_TOKEN environment variable.
--chunkName
Type: String Description: Logical name of the asset chunk (e.g. app, vendor).
Example:
--chunkName="app"
--chunkVersionId
Type: String Description: Version identifier for this chunk (e.g. content hash or build id). Chunks are deduped by (chunkName, chunkVersionId).
Example:
--chunkVersionId="$CI_COMMIT_SHA"
--chunkAssetsDirectory
Type: String (path) Description: Directory whose contents should be packaged into this chunk.
Example:
--chunkAssetsDirectory="dist"
--chunkAssetsDirectoryPrefix
Type: String Description: Path prefix prepended to every entry in the chunk (e.g. static/assets). Files in chunkAssetsDirectory will be served under this prefix at replay time.
Example:
--chunkAssetsDirectoryPrefix="static/assets"
--commitSha
Type: String Description: Commit SHA being tested Default: Auto-detected from git
--force
Type: Boolean Description: Re-upload even if a chunk with the same --chunkName and --chunkVersionId is already marked as uploaded on the server. Use only for recovery (e.g., a corrupted S3 object). The server will overwrite the existing chunk; downstream test runs that already referenced the old bytes will resolve to the new ones. Default: false
Example:
--force
npx @alwaysmeticulous/cli ci upload-asset-chunk \
--apiToken="$METICULOUS_API_TOKEN" \
--chunkName="app" \
--chunkVersionId="$CI_COMMIT_SHA" \
--chunkAssetsDirectory="dist"
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Failure |
| 2 | Error |
Trigger a test run against already-uploaded asset chunks. Pair with ci upload-asset-chunk.
npx @alwaysmeticulous/cli ci run-with-uploaded-asset-chunks \
--apiToken="<token>" \
--commitSha="<sha>" \
--assetReferencesManifest="<path>" \
[options]
--apiToken
Type: String Description: Your Meticulous API token
--assetReferencesManifest
Type: String (path) Description: Path to a JSON file containing a list of { name, versionId } references to previously uploaded asset chunks (see ci upload-asset-chunk). Chunked analog of --appDirectory / --appZip on ci upload-assets.
File format:
[
{ "name": "app", "versionId": "ad8a8da9aaaweaad9" },
{ "name": "plugin-1", "versionId": "dd8ffdaa9dfedebb3" }
]
Example:
--assetReferencesManifest="./manifest.json"
--commitSha
Type: String Description: Commit SHA being tested Default: Auto-detected from git
--baseSha
Type: String Description: The base commit SHA to compare against. Intended for custom test run triggers. Cannot be combined with --repoDirectory.
--gitDiffOutput
Type: String Description: Raw git diff output between the base and head commits. Requires --baseSha. Cannot be combined with --repoDirectory.
--repoDirectory
Type: String (path) Description: The path to a git repository. Intended for custom test run triggers. Automatically infers --commitSha, --baseSha, and --gitDiffOutput from the repo. Cannot be combined with --commitSha, --baseSha, or --gitDiffOutput.
--rewrites
Type: String (JSON) Description: URL rewrite rules in Vercel serve-handler format. Default: '[]' (falls back to { source: "**", destination: "/index.html" })
Example:
--rewrites='[{"source":"/(.*)", "destination":"/index.html"}]'
--sessionFilter
Type: String (path) Description: Path to a JSON file restricting which sessions the test run replays. A session is replayed if its start URL matches at least one of the regexes (RE2 syntax). If omitted, all selected sessions are replayed. See Filter Sessions by Start URL.
File format:
{
"session-start-url-matches-any-regex": ["/checkout/", "/settings/"]
}
Example:
--sessionFilter="./session-filter.json"
--waitForBase
Type: Boolean Description: If true, wait for a base test run to be created before triggering a test run. Default: true
--waitForTestRunToComplete
Type: Boolean Description: Block until the triggered test run finishes. Only for runs tied to a local branch: requires --repoDirectory, or both --baseSha and --gitDiffOutput. Implies --waitForBase. Default: false
--dryRun
Type: Boolean Description: Print what would be triggered without making the API call. Default: false
# manifest.json
# [
# { "name": "app", "versionId": "ad8a8da9aaaweaad9" },
# { "name": "plugin-1", "versionId": "dd8ffdaa9dfedebb3" }
# ]
npx @alwaysmeticulous/cli ci run-with-uploaded-asset-chunks \
--apiToken="$METICULOUS_API_TOKEN" \
--commitSha="$CI_COMMIT_SHA" \
--assetReferencesManifest="./manifest.json"
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Failure |
| 2 | Error |
Replay a session locally for debugging.
npx @alwaysmeticulous/cli simulate \
--sessionId="<id>" \
--appUrl="<url>" \
[options]
--sessionId
Type: String Description: ID of session to replay How to get: From Meticulous dashboard or test run
Example:
--sessionId="ses_abc123..."
--appUrl
Type: String Description: URL where your app is running locally
Example:
--appUrl="http://localhost:3000"
--apiToken
Type: String Description: Your Meticulous API token Note: Required if session is private
--headless
Type: Boolean Description: Run browser in headless mode Default: false
Example:
--headless
--devtools
Type: Boolean Description: Open browser DevTools automatically Default: false
Example:
--devtools
# Basic replay
npx @alwaysmeticulous/cli simulate \
--sessionId="ses_abc123..." \
--appUrl="http://localhost:3000"
# With DevTools for debugging
npx @alwaysmeticulous/cli simulate \
--sessionId="ses_abc123..." \
--appUrl="http://localhost:3000" \
--devtools
# Headless mode for CI
npx @alwaysmeticulous/cli simulate \
--sessionId="ses_abc123..." \
--appUrl="http://localhost:3000" \
--headless
| Code | Meaning |
|---|---|
| 0 | Replay completed successfully |
| 1 | Replay failed |
Start a secure tunnel for manual testing and debugging.
npx @alwaysmeticulous/cli ci start-tunnel \
--port=<port> \
[options]
--port / -p
Type: Number Description: Port your local server is running on
Example:
--port=3000
-p 3000
--apiToken
Type: String Description: Your Meticulous API token Note: Required for authentication
--localHost / -l
Type: String Description: Host to tunnel to Default: localhost
Example:
--localHost=127.0.0.1
--localHttps
Type: Boolean Description: Connect to local HTTPS server Default: false
Example:
--localHttps
--localCert
Type: String (path) Description: Path to SSL certificate file
Example:
--localCert="./certs/server.crt"
--localKey
Type: String (path) Description: Path to SSL key file
Example:
--localKey="./certs/server.key"
--localCa
Type: String (path) Description: Path to CA file for self-signed certificates
Example:
--localCa="./certs/ca.crt"
--allowInvalidCert
Type: Boolean Description: Ignore SSL certificate errors Default: false
Example:
--allowInvalidCert
--proxyAllUrls
Type: Boolean Description: Proxy all URLs through tunnel Default: false
--rewriteHostnameToAppUrl
Type: Boolean Description: Rewrite request hostnames Default: false
--enableDnsCache
Type: Boolean Description: Enable DNS caching Default: false
--printRequests
Type: Boolean Description: Log all requests through tunnel Default: false
Example:
--printRequests
--http2Connections
Type: Number Description: Number of HTTP/2 connections for multiplexing Default: Number of CPU cores
Example:
--http2Connections=8
# Basic tunnel
npx @alwaysmeticulous/cli ci start-tunnel \
--port=3000
# With request logging
npx @alwaysmeticulous/cli ci start-tunnel \
--port=3000 \
--printRequests
# HTTPS tunnel with self-signed cert
npx @alwaysmeticulous/cli ci start-tunnel \
--port=3000 \
--localHttps \
--allowInvalidCert
# Multi-server setup
npx @alwaysmeticulous/cli ci start-tunnel \
--port=3000 \
--proxyAllUrls
When tunnel starts successfully:
Your url is: https://abc123.meticulous.ai
user: meticulous, password: ******
Use these credentials to access your app through the tunnel.
Ensure a base test run exists before running tests.
npx @alwaysmeticulous/cli ci prepare \
--apiToken="<token>" \
[options]
--apiToken
Type: String Description: Your Meticulous API token
--triggerScript
Type: String Description: Path to script that triggers a test run on a specific commit
--headCommit
Type: String Description: Commit SHA to check/prepare Default: Auto-detected
npx @alwaysmeticulous/cli ci prepare \
--apiToken="$METICULOUS_API_TOKEN" \
--triggerScript="./scripts/trigger-test-run.sh"
Set API token via environment variable:
export METICULOUS_API_TOKEN="met_live_abc123..."
# Now can omit --apiToken flag
npx @alwaysmeticulous/cli ci run-with-tunnel \
--appUrl="http://localhost:3000"
GitHub Actions
- name: Run Meticulous tests
run: |
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="${{ secrets.METICULOUS_API_TOKEN }}" \
--appUrl="http://localhost:3000"
GitLab CI
script:
- >
npx @alwaysmeticulous/cli ci upload-assets
--apiToken="$METICULOUS_API_TOKEN"
--appDirectory="dist"
--commitSha="$CI_COMMIT_SHA"
Enable verbose logging:
DEBUG=meticulous:* npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="$METICULOUS_API_TOKEN" \
--appUrl="http://localhost:3000"
Use in shell scripts:
#!/bin/bash
set -e
# Start app
npm start &
APP_PID=$!
# Wait for app
npx wait-on http://localhost:3000
# Run tests
npx @alwaysmeticulous/cli ci run-with-tunnel \
--apiToken="$METICULOUS_API_TOKEN" \
--appUrl="http://localhost:3000"
# Cleanup
kill $APP_PID
Cause: No API token provided
Solution: Pass --apiToken or set METICULOUS_API_TOKEN env var
Cause: App not running or wrong URL
Solutions:
- Verify app is running:
curl http://localhost:3000 - Check port in
--appUrlmatches actual port - Increase wait time before running command
Cause: No recorded sessions for project
Solution: Record sessions first (add recorder snippet to app)
Cause: Network/firewall issue
Solutions:
- Check outbound HTTPS (443) is allowed
- Try
--printRequeststo debug - Contact support if persists
- GitHub Actions Setup - GitHub Actions configuration
- Tunnel Advanced Options - Detailed tunnel configuration
- FAQ & Troubleshooting - Common issues and solutions