Docs / Tools View as Markdown

CLI reference

init (websites, React Native, Expo, iOS, Android), create, start/pause/finish, status, results, analytics, push, pull, login, doctor, skills.

The splithub package is the command line for people (prompts, menus) and for agents (flags, --json). It covers analytics and experiments, plus notes, short links, alerts and allowed domains. Run it with npx splithub or install it as a dev dependency.

npx splithub                 # interactive menu: experiments, analytics, links, alerts, notes, domains
npx splithub --help          # every command and flag

Run commands from the app directory. --json prints one line of JSON; errors are { error, code, hint? } with exit code 1.

Experiments

Command Does
init Writes splithub.config.ts, Next.js middleware and a self-hosted route where applicable, agent skills and MCP entries. Prints the provider snippet for you to add. --mode cloud signs in through a link; --mode self adds the ingest route and a store.
create Adds an experiment to the config. Wizard without flags; flags below. Scaffolds variant components for component. The goal can be any event you track().
start <id> / pause <id> / finish <id> Changes status in the file and applies it live in cloud mode (within a minute). The experiment must already be pushed and credentials available.
status Every experiment with status, visitors, days and the verdict headline; in the cloud also each analytics property's last 7 days.
results <id> Full verdict: sample, comparisons, intervals, P(beat), stars, SRM, metrics, factors. Cloud views below.
events --experiment <id> The latest raw exposures and goals (--type exposure|goal, --limit 50, --before <ms>, --from --to, --filter). 7 days of history, 30 on Pro.
plan Visitors an experiment needs, from SplitHub Cloud, no account needed: --baseline 0.05 --mde 0.1 [--variants 2] [<id>] (with an id, the days at its traffic).
refs <id> Files and lines that use the experiment and its goal.
ship <id> --keep <variant> Finishes the experiment, removes it from the config and rewrites the code to the kept variant. --dry-run shows the diff.
remove <id> Removes an experiment from the config (archived in the cloud when connected).
holdout set <percent> / holdout off Keeps a share of visitors (up to 50%) out of every experiment to measure the long-term impact; --goal <event>.
review [<id>] AI reviews stored in the cloud (Pro): at start, on verdict changes, on finish, or on demand.

create flags

--kind value|component|redirect|mvt  --id <key>  --goal <event>  --status running|draft
--name "Hero headline"  --description "..."  --yes
--variants control,b  --weights 50,50                   # value, component, redirect
--value control="Old text" --value b="New text"          # value (repeatable)
--match /path  --to control=/ --to b=/new                # redirect (repeatable)
--factor headline=old:Old,new:New --factor button=blue:#2563eb,green:#16a34a   # mvt (first level = control)
--traffic 50  --max-visitors 10000  --min-days 3  --good-sample 5000  --start <ISO>  --end <ISO>
--secondary pricing_view,signup  --guardrail refunds:higher,errors
--layer checkout:0-50

--traffic is a percent of visitors. --guardrail goal:higher marks a goal where a rise is bad. --layer key:start-end enrolls buckets start to end (end excluded) of a layer; ranges in one layer must not overlap.

Results views (SplitHub Cloud)

npx splithub results hero --from 2026-10-01 --to 2026-10-15 --filter country=DE   # a slice
npx splithub results hero --by device         # lift per segment
npx splithub results hero --timeline          # visitors and conversions per day
npx splithub results hero --engagement        # bounce, pages per visit, visit length per variant
npx splithub results hero --events            # every goal and event per variant, lift vs control
npx splithub results hero --by country --export > hero.csv

--from / --to are first-exposure days in the project's time zone. Filters and --by dimensions: country, region, city, device, os, browser, language, host, platform, app_version, source, channel, utm_*, landing, page. A slice's verdict covers the slice only and carries no advice. --engagement and the non-goal rows of --events need an analytics property on the same site.

Analytics

Command Does
analytics connect [<id>] Picks or creates a property and adds property="sh-…" to the app's provider, or prints the sh.js tag. --name "Site" creates one, --force replaces another property named there.
analytics Every property with visitors, visits, pageviews and bounce for the last 30 days.
analytics <id> One property: totals, users, goals, top pages, top sources, notes. --by page|source|channel|country|event|goal|…, --by variant --experiment <key>, --by trait --key <name>, --days 7, --from --to, --filter country=DE, --compare [year|<from>..<to>], --segment <name>.
analytics create --name "Site" Adds a property (id generated, like sh-x1h78w); in cloud mode live at once, no push. --kind site|app (default: app in an app project) picks a website or an app property.
analytics pause|resume|archive|restore|remove <id> Stops or restarts collecting, archives (cloud), or removes a property.
analytics events <id> The latest raw pageviews and events, to check tracking.
analytics funnel <id> --steps "page:/,event:Signup,goal:paid" Visitors per step, conversion and drop-off (--within visit|7d|30d, --by).
analytics paths <id> --page /pricing [--prev] Where visitors go next, or came from.
analytics retention <id> Who comes back, per cohort (--start visit|event:<name>|goal:<key>, --return visit|event:<name>, --period day|week, --from --to).
analytics realtime <id> Visitors in the last 5 minutes, top pages and sources.
analytics props <id> --event <name> [--key <prop>] An event's prop names, or one prop's values.
analytics export <id> CSV to stdout (--by <dimension>|day, --raw).
analytics segments Saved filters: --save <name> country=DE …, --delete <id|name>.

properties is an alias of analytics. Self-hosted, analytics reads raw counts from your database; reports with filters, --by, --compare, funnels and retention come with SplitHub Cloud. See Analytics and Reports.

Command Does
notes What happened on a day, marked on experiment timelines and analytics charts (--experiment <key> | --property <id>, --kind note|issue|win, --days 30).
notes add "<text>" [--kind issue|win] [--experiment <key> | --property <id>] [--day YYYY-MM-DD]; notes edit <id> "<text>", notes delete <id>.
links Short links and their clicks; links <id|slug> for one link's clicks.
links create <url> [<url> …] [--slug spring] [--name "Spring sale"] [--weights 70,30]; links edit|pause|resume|delete <id|slug>. See Short links.
alerts Alert rules and recent alerts; alerts enable|disable|delete|test <id>.
alerts add <condition> Adds a rule; conditions and flags in Alerts. Rules run on Pro.
domains Which domains may send events, per experiment and property; domains set experiment|property <id> <domain…> (an app id like com.acme.shop works too), domains clear experiment|property <id>.

Project

Command Does
push [--dry-run] Uploads the normalized config to the cloud. The first push returns the public key and writes it into storage.key. Adopts missing dashboard-created experiments into the file. --dry-run shows what would change. A key that belonged to another experiment stops the push: --replace <keys> (new experiment, old numbers cleared) or --same <keys> (same experiment).
pull Shows the config stored in the cloud and writes missing dashboard-created experiments, including their flows, into the file. Existing entries are not overwritten.
report Status and verdicts as Markdown (raw numbers when self-hosted), with an analytics table in the cloud.
ci init Writes .github/workflows/splithub.yml: checks on pull requests, a results comment on a schedule.
dev [--url] [--hold] Opens the app with ?sh_debug=1 (console and panel: variants, reasons, events) and watches results.
doctor Checks config, provider, middleware, route, code references, endpoint reachability, key.
update [--yes] Updates splithub and the project's @splithub/* packages to the latest release.
skills [--mcp] (Re)installs the agent skills; --mcp also writes .mcp.json and .cursor/mcp.json.
login [--project <name|id>] / logout / whoami Connects a folder to a cloud project through a browser approval; shows the project and its public key.
projects Lists local directories connected to cloud projects.

A push uploads the whole file. An experiment the cloud has from an earlier push that the file lacks is removed; push --dry-run lists it first. Adding analytics never needs a push.

init in detail

npx splithub init --mode cloud --yes                 # link sign-in, then writes everything
npx splithub init --mode cloud --key pk_live_… --yes # public key known; add --no-login to skip browser sign-in
npx splithub init --mode self --db sqlite            # also: postgres | mysql | custom
npx splithub init --no-install --no-login            # files only
npx splithub init --github-action                    # also writes the GitHub workflow

init looks at the project first:

Project What init does
Next.js, Vite, other React writes the config and framework files, prints the provider snippet
React Native, Expo installs @splithub/react-native and its storage module (npx expo install on Expo), wraps app/_layout.tsx (Expo Router, adds <ExpoRouterScreens />) or App.tsx (adds <NavigationScreens /> with React Navigation); suggests the cloud
iOS (Xcode), Android (Gradle) prints the Swift package or Gradle line and a quick start; always the cloud, no config file

With --json, app.entry.status says whether the React Native entry was wrapped (added, already, or manual with a nextStep). doctor checks the package, the storage module and the provider in a React Native app, and the SDK in the build files of a native one. See Apps.

The public key ends up in storage.key from the first of: --key, the saved credentials of login, SPLITHUB_API_KEY in the environment (init asks the cloud which project it belongs to), or the first push. init prints where the key came from. --no-mcp skips the MCP entries.

Where results come from

storage.mode status / results read from Auth
self your app's handler on the local dev server (localhost:3000 for Next.js, 5173 for Vite) or --url / SPLITHUB_URL; raw numbers only --token / SPLITHUB_ADMIN_TOKEN when the handler has adminToken
cloud https://go.splithub.io/v1 (or --api / SPLITHUB_API) the key saved by login, SPLITHUB_API_KEY, or --token sk_…

Credentials

login prints a code and a URL. You approve it in the dashboard (and can create the project right there); the dashboard mints a project API key for that terminal and the CLI stores it per directory in ~/.config/splithub/credentials.json (%APPDATA%\splithub on Windows). Nobody pastes a secret into a chat. CI and headless agents use SPLITHUB_API_KEY=sk_… from the project's Settings → API keys.

pk_… is public: it sends events and reads client config. sk_… reads results and pushes config: keep it out of git and out of the browser. An API key can be limited to some areas (experiments, analytics, events, notes, links, alerts, domains, each read or write); a command it may not run fails with INSUFFICIENT_SCOPE and names the missing scope.

Exit codes and JSON

0 success, 1 error (with --json, the error is printed as { error, code, hint? }). status --json returns { project, experiments: [{ experiment, kind, status, goal, name, exposures, visitors, conversions, days, verdict, headline, best, sample, stars, voice, daysToVerdict, display }] }, plus properties in the cloud; results --json returns the full results object described in Statistics.

SplitHub
See what works. Ship what wins.