# CLI reference

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.

```bash
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

```bash
--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)

```bash
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](/docs/analytics) and [Reports](/docs/analytics-reports).

## Notes, links, alerts, domains (SplitHub Cloud)

| 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](/docs/links). |
| `alerts` | Alert rules and recent alerts; `alerts enable\|disable\|delete\|test <id>`. |
| `alerts add <condition>` | Adds a rule; conditions and flags in [Alerts](/docs/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

```bash
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](/docs/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](/docs/statistics).
